React 中 useMemo 和 useCallback 什么时候该用什么时候不该用?
React 中 useMemo 和 useCallback 什么时候该用什么时候不该用一句话总结不是所有函数和对象都要包一层。只有「传给子组件的引用」或「依赖数组的依赖项」需要稳定引用时才用否则纯属添乱正文目录useMemo / useCallback 到底解决什么问题5 大高频误用场景 修复代码万能兜底三个问题判断该不该用预防 checklist不再踩坑一句话总结一、useMemo / useCallback 到底解决什么问题useMemo缓存计算结果useCallback缓存函数引用。// useMemo缓存计算结果 const expensiveValue useMemo(() compute(a, b), [a, b]); // useCallback缓存函数引用 const handleClick useCallback(() doSomething(id), [id]);本质它们解决的不是性能问题而是引用稳定性问题。React 组件每次渲染函数体内的变量和函数都会重新创建。如果你把这些新创建的函数/对象传给子组件或放进依赖数组子组件就会不必要地重渲染effect 也会不必要地重复执行。一句话引用不稳定 → 子组件白渲染 / effect 白执行 → 才需要 useMemo/useCallback。二、5 大高频误用场景 修复代码① 给没有性能问题的计算包了 useMemo// ❌ 简单计算包 useMemo缓存本身比计算还贵 function UserList({ users }) { const count useMemo(() users.length, [users]); // ❌ 求个长度还缓存 return div共 {count} 人/div; }修复直接算// ✅ 简单计算不需要缓存 function UserList({ users }) { const count users.length; // ✅ 直接取 return div共 {count} 人/div; }② 给不传给子组件的函数包了 useCallback// ❌ 函数只在当前组件用包了 useCallback 没意义 function Form() { const [value, setValue] useState(); const handleChange useCallback((e) { // ❌ 没传给子组件纯浪费 setValue(e.target.value); }, []); return input value{value} onChange{handleChange} /; }修复原生元素不需要稳定引用// ✅ 原生 input 不需要 useCallback function Form() { const [value, setValue] useState(); const handleChange (e) { // ✅ 直接定义 setValue(e.target.value); }; return input value{value} onChange{handleChange} /; }③ 传给 memo 子组件的函数没包 useCallbackmemo 白写了// ❌ 子组件用了 memo但父组件每次传新函数引用 → memo 失效 const Child memo(({ onClick }) { console.log(Child rendered); // ❌ 每次父组件渲染都触发 return button onClick{onClick}Click/button; }); function Parent() { const [count, setCount] useState(0); const handleClick () { // ❌ 每次渲染新函数 console.log(clicked); }; return ( button onClick{() setCount(c c 1)}1/button Child onClick{handleClick} / / ); }修复用 useCallback 稳定引用// ✅ useCallback 稳定引用memo 生效 const Child memo(({ onClick }) { console.log(Child rendered); // ✅ 只有 props 变才渲染 return button onClick{onClick}Click/button; }); function Parent() { const [count, setCount] useState(0); const handleClick useCallback(() { // ✅ 引用稳定 console.log(clicked); }, []); return ( button onClick{() setCount(c c 1)}1/button Child onClick{handleClick} / / ); }④ useCallback 的依赖项写错闭包陷阱// ❌ 依赖数组写了空但函数里用了 state → 闭包旧值 function Timer() { const [count, setCount] useState(0); const handleTick useCallback(() { console.log(count); // ❌ 永远打印 0闭包陷阱 setCount(count 1); }, []); // ❌ 空依赖 }修复方案一把依赖加上// ✅ 加上依赖 const handleTick useCallback(() { console.log(count); setCount(count 1); }, [count]); // ✅ count 变化时更新修复方案二用函数式更新// ✅ 函数式更新不需要 count 依赖 const handleTick useCallback(() { setCount(prev prev 1); // ✅ 不依赖外部 count }, []);⑤ useMemo 缓存对象但依赖项是引用不稳定的// ❌ 依赖项是每次新建的对象 → useMemo 每次都重新计算 function Search({ query }) { const params { keyword: query, page: 1 }; const results useMemo(() { return fetchData(params); // ❌ params 每次新对象 → memo 失效 }, [params]); // ❌ params 引用不稳定 }修复依赖原始值而非对象// ✅ 依赖原始值而非对象 function Search({ query }) { const results useMemo(() { return fetchData({ keyword: query, page: 1 }); // ✅ 对象在内部创建 }, [query]); // ✅ 只依赖原始值 }三、万能兜底三个问题判断该不该用问自己三个问题1. 这个函数/对象会被传给子组件吗 ├── 否 → 不需要用 └── 是 → 看第 2 题 2. 子组件用了 memo 吗 ├── 否 → 不需要用 └── 是 → 看第 3 题 3. 这个函数/对象会作为 useEffect 的依赖吗 ├── 否 → 可能不需要用除非 memo 子组件 └── 是 → 必须用 useCallback/useMemo 稳定引用快速参考表场景需要 useMemo/useCallback原生元素的事件处理❌ 不需要简单计算加减乘除、取属性❌ 不需要传给 memo 子组件的 props✅ 需要useEffect 的函数/对象依赖✅ 需要大量数据处理排序、过滤大数据集✅ 需要只在当前组件用的函数❌ 不需要传给第三方组件的高频回调✅ 可能需要万能模板import { useState, useCallback, useMemo, memo } from react; // ✅ 子组件 memo const ExpensiveChild memo(({ data, onUpdate }) { return div{data.map(item div key{item.id}{item.name}/div)}/div; }); function Parent() { const [keyword, setKeyword] useState(); const [rawData, setRawData] useState([]); // ✅ 传给 memo 子组件 → 用 useCallback const handleUpdate useCallback((id) { setRawData(prev prev.map(item item.id id ? { ...item, name: updated } : item )); }, []); // ✅ 大量数据过滤 → 用 useMemo const filteredData useMemo(() { return rawData.filter(item item.name.includes(keyword)); }, [rawData, keyword]); // ✅ 依赖原始值 return ( input value{keyword} onChange{e setKeyword(e.target.value)} / ExpensiveChild data{filteredData} onUpdate{handleUpdate} / / ); }四、预防 checklist不要无脑包原生元素的事件、简单计算不需要 useMemo/useCallbackmemo 子组件的 props 必须稳定传函数用 useCallback传对象用 useMemouseEffect 的依赖是函数/对象时必须用 useCallback/useMemo 稳定引用依赖数组写原始值别把引用不稳定的对象放进依赖数组闭包陷阱useCallback 里用了 state 就要加依赖或用函数式更新先写对再优化先让代码跑起来出现不必要的重渲染再加 memo useCallbackuseCallback 的依赖和 useMemo 一样变了就重建没变就复用五、一句话总结「useMemo / useCallback 解决的是引用稳定性不是性能」。只有传给 memo 子组件的 props 或作为 effect 依赖时才需要其他场景直接定义。先写对再优化别上来就全包一层最后问候亲爱的朋友们并邀请你们阅读我的全新著作 《React 进阶实战》