useCallback:缓存回调函数
在 React 函数组件中,每一次 UI 的变化,都是通过重新执行整个函数来完成的
如果组件中依赖的处理函数没有被useCallback做缓存处理,那么每次重新执行时都会被重新创建
那么依赖处理函数的组件就会因为处理函数更新进行重现渲染
为避免频繁的重新渲染组件,useCallback的作用就是将函数缓存起来,
只有当函数逻辑中依赖的状态发生变化才会去重新生成,然后引起组件更新,
避免了依赖没有变化时的无效更新
useCallback 可以减少不必要的渲染,主要体现在将回调函数作为属性传给某个组件。
如果每次都不一样就会造成组件的重新渲染。
但是如果确定子组件多次渲染也没有太大问题,特别是原生的组件,比如 button,
那么不用 useCallback 也问题不大。所以这和子组件的实现相关,和函数是否轻量无关。
useMemo:缓存计算的结果
同处理函数类似,如果组件中用到的数据A通过其他状态值计算得到,
在依赖值没有发生变化情况下,其实就不用重新计算,
即
如果某个数据是通过其它数据计算得到的,那么只有当用到的数据,也就是依赖的数据发生变化的时候,才应该需要重新计算
因此当组件因为其他原因刷新,进而重新执行函数,引起的重新计算是没有必要的
可以实现这个功能的hooks就是useMemo,useMemo就是只有在依赖的状态值发生变化时才会去重新执行计算的过程
避免不必要的重新计算过程
同时,对于依赖数据A的组件来说,没有重新计算产生的新值,可以在一定程度上避免子组件重复渲染。
如果将函数也看做一个状态值/变量的话,其实,useMemo可以实现useCallback的功能
1 | const myEventHandler = useMemo(() => { |
二者的本质就是
建立了一个绑定某个结果到依赖数据的关系。只有当依赖变了,这个结果才需要被重新得到。
useRef:在多次渲染之间共享数据
可以把 useRef 看作是在函数组件之外创建的一个容器空间
在这个容器上,我们可以通过唯一的 current 属设置一个值,从而在函数组件的多次渲染之间共享这个值
使用 useRef 保存的数据一般是和 UI 的渲染无关的,因此当 ref 的值发生变化时,是不会触发组件的重新渲染的,
这也是 useRef 区别于 useState 的地方。
比如保存定时器句柄,在回调函数中关掉定时器,然后将ref.current 设置为null, 这个句柄没有在组件中被用到,所以不会引起重新渲染。
但如果用useState去保存句柄,将定时器关掉时,同时将state设置为null,即使没有被组件用到,也会引起重新组件渲染。
useRef 还有一个重要的功能,就是保存某个 DOM 节点的引用
1 |
|
可以看到 ref 这个属性提供了获得 DOM 节点的能力,并利用 useRef 保存了这个节点的应用
useContext:定义全局状态
1 |
|
Context 提供了一个方便在多个组件之间共享数据的机制。
不过需要注意的是,它的灵活性也是一柄双刃剑。
你或许已经发现,Context 相当于提供了一个定义 React 世界中全局变量的机制,
而全局变量则意味着两点:
- 会让调试变得困难,因为你很难跟踪某个 Context 的变化究竟是如何产生的。
- 让组件的复用变得困难,因为一个组件如果使用了某个 Context,它就必须确保被用到的地方一定有这个 Context 的 Provider 在其父组件的路径上。
需要再三强调的是,Context 更多的是提供了一个强大的机制, 让 React 应用具备定义全局的响应式数据的能力。