React--常用 hook
一些 React 使用中常见的 hook
useState
最常见的 state hook 没什么好说的,每个学 react 的人第一个钩子
主要说明一下
React 18+ 批处理:将多个状态更新 (setState 调用) 合并为一个单一的更新批次,一起渲染。这样可以减少重新渲染的次数,从而提升性能
1 | function App() { |
在旧版 React 中, setCount 和 setName 会在两次渲染中分别出发,即每次更新都会造成一次组件渲染
而在 React 18+, setCount 和 setName 会在被收集到一次渲染队列中,一起提交给后续渲染
1 | const [cnt, setCnt] = setState(0); |
上述代码 cnt 变量并没有+2,而是+1,因为被批处理了,设置 Cnt 状态基于当前的 cnt,而第一次设置完后并没有立即渲染,所以第二次渲染的时候 cnt 还是 1+1 = 2
如果有这种依赖关系的渲染,请你使用回调函数,保证每次 setCnt 都会触发一次渲染
useRef
useRef 允许你创建一个持久化的引用,用于访问组件中的某个值或者 DOM 元素,并且不会触发重新渲染。可以说,useRef 是一个用于在多个渲染周期之间”存储数据”的容器。
工作原理
useRef 返回一个包含 current 属性的对象。这个对象在组件的整个生命周期都是持久化的,无论组件渲染多少次,它都指向一个相同的对象。
使用
- 请看如下代码
1 | import React, { useRef, useEffect } from "react"; |
首先第一次渲染函数组件,返回一个绑定了 ref 的 input 元素。
之后再次渲染,对于 html 元素, react 采取的策略是如果元素的 state 和 props 没有发生改变,那么就不会创建一个新的元素,而是复用之前的元素
详细说说,第一次返回了一个带有 ref 的标签, ref 指向了 input 元素,第二次 useRef 返回的还是指向之前 input 元素的引用,而第二次 input 标签的 state,props 没有变化,所以使用的还是之前的 input 完成了 DOM 元素的记录
- 请看如下代码
1 | import React, { useState } from "react"; |
每次点击,触发状态更新,传递一个新的状态数值给Timer组件,会重新调用 Timer 组件函数,里面的 useRef,useState 等钩子函数行为都是,查看当前 fiber tree 上是否有该 state,该 ref,如果有返回当前的 state,ref,如果没有,则创建对象并返回
所以上述 timer 运行原理,第一传入一个 count, 触发渲染,触发 useEffect,设置 current 但是设置完 ref.current 并不会触发重新渲染,故当前 ref.current 还是之前的值,undefined,就完成了记录,当前值和之前值
如果再次触发 count 改变的渲染,那么重复上面的过程,返回的对象是相同的,修改 ref.current 的数值,并不会立即触发渲染,所以又保存了之前的状态
- 当然还有一个应用,背后记录一些数据,不展示在页面上,不要重新触发渲染
…
保持不变的 react key
在动态渲染列表的时候,例如map(e=><li></li>) 如果不加 key, 那么 react 会根据元素的位置来判断元素是否和之前的元素保持一样,这样的做法会产生一些问题
例如,当删除列表中间的一个元素,那么后面的元素的位置都发生了变化,后面元素的 state 都将发生改变,不再保留之前的状态,这样会导致一些意想不到的错误。 但是如果加上key属性值,那么,key 值不变,react 就不会重新渲染这个元素,从而保持元素状态稳定性
useEffect / useLayoutEffect
要想说明白 useEffect, useLayoutEffect 必须先要理解 React 的渲染过程
jsx -> React.createReactelement -> virtual dom -> fiber tree -> commit -> dom -> useLayoutEffect return function -> useLayoutEffect -> 浏览器的绘制 -> useEffect return function -> useEffect
其中 useLayoutEffect 表示 useLayoutEffect 在 fiber tree 中注册的调用函数
其中 useLayoutEffect return function 表示 useLayoutEffect 返回的函数执行
其中 useEffect return function 表示 useEffect 返回的函数执行
其中 useEffect 表示 useEffect 在 fiber tree 中注册的调用函数
useLayoutEffect 注册函数发生在浏览器绘制之前,所以一定是一个同步函数,常用于测量一些浏览器 DOM 相关内容
而 useEffect 在浏览器绘制之后,常用来注册一些异步函数,比如请求数据等
useMemo / useCallback
两个钩子都用于性能优化,缓存计算结果和避免不必要的函数重新创建,从而避免不必要的重新渲染和计算。
useMemo: 缓存计算结果,只有在依赖项发生变化时才重新计算值
useCallback: 缓存函数,只有在依赖项发生变化时才重新创建函数
模式都是一样的
1 | const memoizedValue = useMemo(() => { |
如果依赖项 dependency1,dependency2 不发生变化,那么就不会执行 memo 中注册的函数
如果依赖项 count 不发生变化,那么就不会返回 useCallback 中创建的函数
往往配合 useCallback 创建的函数放在 useMemo 的依赖项中
useContext
用于在函数组件中,订阅 React 上下文的变化,组件书中跨越多个层级传递数据,而不必通过层层的 props 传递
基于声明式标签,解决 props 钻问题
