04|内置 Hooks(2):为什么要避免重复定义回调函数?
useCallback:缓存回调函数
- 深入了解
- 翻译
- 解释
- 总结
React内置Hooks useCallback、useMemo、useRef和useContext的用法及作用。通过实际例子生动地阐述了它们的使用场景和优势。useCallback和useMemo的作用是缓存回调函数和计算结果,避免不必要的函数重复创建和组件重新渲染。useRef用于在函数组件之间共享数据,以及保存DOM节点的引用。而useContext则能定义全局状态,实现跨层次或同层组件之间的数据共享。文章还提到了Context的灵活性和一些注意事项。总结来说,本文详细介绍了React内置Hooks的使用方法和原理,对于React函数组件的性能优化和全局状态管理具有重要的指导意义。
《React Hooks 核心原理与实战》,新⼈⾸单¥59
全部留言(50)
- 最新
- 精选
- 何以解忧置顶关于子组件props 不变,可以减少不必要的渲染问题,不是特别理解。似乎只要父组件重新渲染子组件必然重新渲染,是内部有什么别的地方优化么?
作者回复: 好问题,之前在 Class 组件中可以把组件继承自 React.PureComponent,从而 props 没变就不重新 render。现在函数组件没有 PureComponent的概念,但是提供了 React.memo (https://reactjs.org/docs/react-api.html#reactmemo) 这样一个高阶组件,可以让任何 React 组件都能在 props 不变时就不重新渲染。所以,在开发过程中,即使现在没有使用 React.memo,但是使用 useCallback 或者 useMemo 至少可以为性能优化提供一个基础。
2021-06-10917 - 满月我们能否用 state 去保存 window.setInterval() 返回的 timer 呢? 我理解的是可以,只是没有 useRef 更优,因为在更新 state 值后会导致重新渲染,而 ref 值发生变化时,是不会触发组件的重新渲染的,这也是 useRef 区别于 useState 的地方。
作者回复: 100分~
2021-06-01368 - 桃翁useRef 如果只是用来 在多次渲染之间共享数据,是不是直接可以把变量定义到组件外面,这样也可以达到目的,感觉还更方便一点呢。
作者回复: useRef 可以保证这个变量只在当前组件的实例中使用。也就是说,如果一个组件页面上有多个实例,比如: <div><Timer /><Timer /></div> 那么组件外的普通变量是被 Timer 共享的,就会产生问题。
2021-06-01529 - cyh41是任何场景 函数都用useCallback 包裹吗?那种轻量的函数是不是不需要?
作者回复: 确实不是,useCallback 可以减少不必要的渲染,主要体现在将回调函数作为属性传给某个组件。如果每次都不一样就会造成组件的重新渲染。但是如果你确定子组件多次渲染也没有太大问题,特别是原生的组件,比如 button,那么不用 useCallback 也问题不大。所以这和子组件的实现相关,和函数是否轻量无关。但是比较好的实践是都 useCallback。
2021-06-01415 - 七月有风问下老师,useCallback、useMemo 和 useEffect的依赖机制一样吗?都是浅比较吗?
作者回复: 是的,所以依赖比较都是浅比较
2021-06-0326 - Geek_71adef1 useState 实现组件共享,考虑到组件之间的通信 2 state 去保存的话 会造成异步渲染 造成无限循环
作者回复: 只有需要触发 UI 更新的状态才需要放到 state 里。这里的 timer 其实只是临时存放一个变量,无需用 state 保存。否则会造成不必要的渲染。
2021-06-016 - 院长。有个问题想问下,关于useMemo,文档说的是性能优化的保证,也就是涉及到大量计算的时候可以使用,因为依赖项的比较本身也是有开销的。 那如果我就只是很简单的计算,或者就只是返回一个固定的对象,有必要使用吗
作者回复: 依赖项比较大的性能开销可以忽略。useMemo 其实除了解决自身计算的性能问题之外,还有就是可以避免 接收这个数据的组件过多的重新渲染,以及依赖这个数据的其它 hooks 多余的计算。所以即使简单的计算,最好也是用 useMemo。
2021-06-114 - 闲闲useCallBack依赖是空数组表示什么?
作者回复: 没有意义,相当于每次都创建一个新的函数
2021-06-0174 - 开开之之老师,我也有同样的疑问,定时器的例子,不能用一个常量去保存吗? import React, { useState, useCallback, useRef } from 'react' export default function Timer() { const [time, setTime] = useState(0) const timer = useRef(null) let timer2 = null const handleStart = useCallback(() => { timer2 = window.setInterval(() => { // 这里是个闭包,每次拿到的time值是0,所以要这样写手动去更新time的值 setTime((time) => time + 1) }, 1000) }, [time]) const handleStop = useCallback(() => { window.clearInterval(timer2) timer2 = null }, []) return ( <div> {time / 10} seconds. <br/> <button onClick={handleStart}>start</button> <button onClick={handleStop}>stop</button> </div> ) }
作者回复: 你这个写法,看上去能够 work 是因为 handleStop 没有将 timer2 设为依赖项,eslint 会报错。其实在你第二次 start 后就无法 stop 了。因为 timer 一直是第一次的值。
2021-06-0652 - 琼斯基亚老师,请问: const handleIncrement = useCallback(() => setCount(count + 1), [count]); const handleIncrement = useCallback(() => setCount(q => q + 1), []); 在性能方面是否后者优于前者?我的理解: 后者只创建了一次函数,但是又调用了多次在setCount的回调函数 前者只会在count变化的时候创建新的回调函数 这样分析下来我又觉得两者没什么差异 我不是太清楚这两者的优缺点,希望得到老师的解答。
作者回复: 严格来说,后者确实优于前者,因为后者在 count 变化时不会创建新的 handleIncrement 这样的 callback,这样接收这个属性的组件就不需要重新刷新。但是对于简单的场景,可以忽略这种差异。
2021-06-0341