React 各个Hooks

200 阅读4分钟

Hooks 总览

image.png

useState

使用状态

const [n,setN] = React.useState(0)

cosnt [user,setUser] = React.useState({name:Origammi})

注意事项

  • 如果state是一个对象,那么是不能部分setState的

image.png

因为useState不会帮我们合并属性,这样的话会把age给覆盖掉,正确做法应该是如下图

image.png

将旧的user用...user进行拷贝,随后用新的name来对旧的进行覆盖,就不会把age覆盖掉了。

  • 如果setState(obj),如果obj的地址不变,那么React认为数据就没有变化
useState和setState接受函数

image.png 这里面只有最后一次的setN是生效的,正确做法是改成函数,传一个操作给它,第一次set完以后会把新的值赋给第二次的set。

image.png

useReducer

践行Flux/Redux的思想

四步走

  1. 创建初始值initialState

  2. 创建所有操作 reduce(state,action)

  3. 传给useReducer,得到读和写API

  4. 调用写({type:'操作类型'})

将上面的例子进行改写

image.png

可以看得出来useReducer就是useState的复杂版

如何替代Redux
  1. 将数据集中在一个store对象

  2. 将所有操作集中在reducer

  3. 创建一个Context

  4. 创建对数据的读写API

  5. 将读写的API放入Context中

  6. 用Context.Provider将Context的内容提供给所有组件

  7. 各个组件用useContext获取读写API!

image.png

useContext

上下文

全局变量是全局的上下文

上下文是局部的全局变量

使用

上图的例子已经使用过了

  1. C = creatContext(initial)创建上下文

  2. <C.Provider>圈定作用域

  3. 在作用域内使用useContext(C)来使用上下文

示例如下: image.png

我们在<C.Provider> value={}中会给一个初始值一般是一个读接口和一个写接口。

注意事项 useContext并非响应式,在一个模块中将C的值改了,另一个模块是不会有感知的。

useEffect

副作用

对环境的改变就是副作用,比如修改document.title

但是不一定非要把副作用放在useEffect里面

实际上叫做afterRender更好每次render之后运行

用途

作为componentDidMount使用,[]为第二个参数

作为componentDidUpdate使用,可指定依赖

作为componentWillUnmount使用,通过return

以上三种用途可以同时存在

如果存在多个useEffect,会按照出现次序进行

useLayoutEffect

布局副作用

useEffcet在浏览器渲染完成后执行,

useLayoutEffect在浏览器渲染前执行

特点

useLayoutEffect总是比useEffect先执行

useLayoutEffect里面的任务最好是影响了Layout

建议

为了用户体验,优先使用usetEffect(优先渲染)

useMemo

先说React.memo,(一般先memo再useMemo)看如下代码

image.png

其中Child组件props的并没有变化,但是child依然执行了,这就是多余的render。

如果想要优化,可以使用React.memo对Child组件进行封装

image.png

就可以了。

但是它有一个问题:添加了监听函数就会失效,因为App执行时会生成一个新的函数,虽然功能一样但是地址不同,这时需要使用useMemo

image.png

特点
  • 第一个参数是()=>value

  • 第二个参数是依赖[m,n]

只有当依赖变化时,才会计算出新的value

如果依赖不变,那就重用之前的value

与Vue2的computed相似

注意

如果value是个函数就要写成useMemo(()=>(x)=>console.log(x))这样返回函数的函数。

useCallback

为了解决useMemo返回函数的函数这样的写法,诞生了useCallback

useCallback(x=>console.log(x),[m])等价于useMemo(()=>x=>console.log(x),[m])

useRef

目的

如果需要一个值,在组件不断render时保持不变,则使用useRef

初始化 const count = useRef(0)

读取 count.current

为了保证两次useRef是同一个值,需要current

image.png

想要记录n变化了多少次,那么就可以利用useRef。

forwardRef

image.png

我们希望能使用buttonRef能引用到button2对应的DOM对象,但是这样会报错,警告函数组件不能接受ref,报错内容会提示使用forwardRef

image.png

可以看到,由于props不包含ref,所以需要forwardRef

image.png 使用forwarfRef将button2包装起来起名button3,可以把外边的ref转发给button3第二个参数。再return时传给button

但是大部分情况下props不需要包含ref

自定义Hook

封装数据操作

image.png

自定义一个函数useList,并且把读写接口return出去,这样外面的组件就可以知道list的变化。

image.png

除了读写接口还可以加入增删的借口供给外部调用

image.png

还可以在自定义hook里面使用Context

Stale Closure

过时闭包