useCallback的基本使用
函数式组件中,useCallback对函数进行缓存,组件再次更新时会根据依赖是否变化决定选用缓存函数还是新函数。一般会在嵌套组件中,与函数式组件的memo和类组件的PureComponent一起使用【会对传入props参数逐个进行浅比较决定是否需要更新】,来提高页面性能。
函数式组件中,使用useCallback对函数进行缓存(被外层函数包裹,相当于闭包),组件再次更新时(函数重新执行)会根据依赖是否变化决定选用缓存函数【之前生成的函数】还是新函数【新生成的上下文】。
一般会在嵌套组件中,与函数式组件的memo和类组件的PureComponent一起使用【会对传入props参数逐个进行浅比较决定是否需要更新】,来提高页面性能。
-
传入箭头函数还是普通函数没有影响
-
useCallback第二个参数决定组件更新是否生成新函数,函数内部使用到的依赖尽可能在第二个参数写全,否则会有意想不到的问题(序列4)。
何时生成新函数 第二个参数 组件首次执行及更新都会生成新函数 空 组件首次执行生成,之后不变 [] 组件首次执行、依赖变化时生成新函数 [state, ref.current] 这两类 -
嵌套组件中 memo搭配useCallback和不使用useCallback对子组件更新的影响
(情况1)不使用useCallback时,外层组件每次更新相当于重新生成新函数funcHang,传给子组件。子组件通过memo包裹对传入props参数进行浅比较,发现onClick触发的函数有变化,进而子组件更新。
(情况2)使用useCallback时,第二参数为[]
,外层组件每次更新时(暂时不考虑依赖)都会复用上次函数。子组件接收到传入props的funcHang没有变化,所以子组件不会发生更新。
-
setxxx更新逻辑:
当setxxx触发更新时,App函数重新执行(相当于新的作用域),当执行到const funcHang = useCallback(...);
useCallback内部会比较之前依赖preDeps
和现在依赖nextDeps
是否相等if (areHookInputsEqual(nextDeps, prevDeps))
;相等true直接返回之前函数,不相等false就返回最新传入的函数。
以下是依赖没写全,导致函数没有及时更新。访问不到最新的数据问题:
-
(步骤0)代码及初始界面: useCallback函数内部使用了count1和count2,可依赖只有count1:
export function App() { const [count1, setCount1] = useState(0); const [count2, setCount2] = useState(0); // 当count2发生变化时,触发funcHang内部count2是0;除非count1变化引起生成新函数count2才是最新值 const funcHang = useCallback(function() { console.log("function run:", count1, count2); }, [count1]); return ( <div> <h2>params: { count1 } { count2 }</h2> <button onClick={ funcHang }>触发</button> <button onClick={ e => { setCount1(pre => pre + 1);} }>update Count1</button> <button onClick={ e => { setCount2(pre => pre + 1);} }>update Count2</button> </div> ) }
-
(步骤1)点击按钮update Count1,再触发。打印输出
function run: 1 0
-
(步骤2)点击按钮update Count2,再触发。打印输出
function run: 1 0
-
(步骤3)再次点击按钮update Count1,再触发。打印输出
function run: 2 1
分析:
- 当count1变化,useCallback内部依赖比较发生变化,返回false,返回当前执行App上下文包裹的函数。
- 当count2变化,useCallback内部依赖比较没有变化,返回true,返回之前App上下文包裹的函数。
总结:函数使用的是生成时所处App上下文中的数据,注意函数何时会被更新;
更多推荐
所有评论(0)