React
useCallback 稳定函数引用,让 memo 子组件跳过无关更新。
在线预览
- 机械键盘 · ¥499
- 无线鼠标 · ¥299
- 显示器支架 · ¥359
当前选择
尚未选择商品
点击列表中的按钮选择商品。
更新购物车会重新渲染父组件,但稳定的 onSelect 不会破坏 memo 缓存。
组件关系与数据流
StateComponent
差异焦点函数组件反复执行,需要 useCallback 配合 memo 维持 props 引用稳定。
实线表示数据向下传递,虚线表示子组件向父组件发出通知。
flowchart TB
accTitle: React useCallback 与 memo 引用比较
accDescr: 任意 state 更新都会重新执行 ReactDemo,useCallback 根据依赖数组复用 handleSelect 引用,memo 再浅比较 props 决定是否跳过 ProductList 渲染。
state(["selectedProduct + cartCount"]) --> render["ReactDemo 每次 state 更新都执行"]
render --> callback{{"useCallback([]) 复用函数引用"}}
render --> compare{{"memo 浅比较全部 props"}}
callback --> compare
compare -->|"props 变化才执行"| child["ProductList (memo)"]
child -.->|"onSelect(product)"| state
class state state
class render,child component
class callback,compare process
classDef state fill:#ffffff,stroke:#149eca,color:#172033,stroke-width:1.2px
classDef component fill:#e5f6fb,stroke:#149eca,color:#172033,stroke-width:2px,font-weight:600
classDef process fill:#f4fbfd,stroke:#149eca,color:#172033,stroke-width:1.6px
classDef resource fill:#f3f5f8,stroke:#778195,color:#172033,stroke-width:1.4px组件每次 state 更新都会执行;useCallback 稳定函数引用,memo 再比较 props。
实际运行源码
import { useCallback, useState } from "react";import { ProductList, type Product } from "./ProductList";
export function ReactDemo() { const [cartCount, setCartCount] = useState(0); const [selectedProduct, setSelectedProduct] = useState<Product | null>(null);
const handleSelect = useCallback((product: Product) => { setSelectedProduct(product); }, []);
return ( <div className="demo-stack"> <ProductList selectedId={selectedProduct?.id ?? null} onSelect={handleSelect} />
<div className="demo-card"> <span className="demo-tag">当前选择</span> <h2>{selectedProduct?.name ?? "尚未选择商品"}</h2> <p> {selectedProduct ? `价格 ¥${selectedProduct.price}` : "点击列表中的按钮选择商品。"} </p> </div>
<div className="demo-actions"> <button className="demo-button" type="button" onClick={() => setCartCount((count) => count + 1)} > 购物车 +1(当前 {cartCount}) </button> </div> <p className="demo-muted"> 更新购物车会重新渲染父组件,但稳定的 onSelect 不会破坏 memo 缓存。 </p> </div> );}import { memo } from "react";
export type Product = { id: number; name: string; price: number;};
const products: Product[] = [ { id: 1, name: "机械键盘", price: 499 }, { id: 2, name: "无线鼠标", price: 299 }, { id: 3, name: "显示器支架", price: 359 },];
type Props = { selectedId: number | null; onSelect: (product: Product) => void;};
export const ProductList = memo(function ProductList({ selectedId, onSelect,}: Props) { return ( <ul className="demo-list"> {products.map((product) => ( <li key={product.id}> <span> {product.name} · ¥{product.price} </span> <button className="demo-button" type="button" aria-pressed={selectedId === product.id} onClick={() => onSelect(product)} > {selectedId === product.id ? "已选择" : "选择"} </button> </li> ))} </ul> );});