Hooks 总览
useState
使用状态
const [n,setN] = React.useState(0)
cosnt [user,setUser] = React.useState({name:Origammi})
注意事项
- 如果state是一个对象,那么是不能部分setState的
因为useState不会帮我们合并属性,这样的话会把age给覆盖掉,正确做法应该是如下图
将旧的user用...user进行拷贝,随后用新的name来对旧的进行覆盖,就不会把age覆盖掉了。
- 如果setState(obj),如果obj的地址不变,那么React认为数据就没有变化
useState和setState接受函数
这里面只有最后一次的setN是生效的,正确做法是改成函数,传一个操作给它,第一次set完以后会把新的值赋给第二次的set。
useReducer
践行Flux/Redux的思想
四步走
-
创建初始值initialState
-
创建所有操作
reduce(state,action) -
传给useReducer,得到读和写API
-
调用写
({type:'操作类型'})
将上面的例子进行改写
可以看得出来useReducer就是useState的复杂版
如何替代Redux
-
将数据集中在一个store对象
-
将所有操作集中在reducer
-
创建一个Context
-
创建对数据的读写API
-
将读写的API放入Context中
-
用Context.Provider将Context的内容提供给所有组件
-
各个组件用useContext获取读写API!
useContext
上下文
全局变量是全局的上下文
上下文是局部的全局变量
使用
上图的例子已经使用过了
-
C = creatContext(initial)创建上下文 -
<C.Provider>圈定作用域 -
在作用域内使用
useContext(C)来使用上下文
示例如下:
我们在<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)看如下代码
其中Child组件props的并没有变化,但是child依然执行了,这就是多余的render。
如果想要优化,可以使用React.memo对Child组件进行封装
就可以了。
但是它有一个问题:添加了监听函数就会失效,因为App执行时会生成一个新的函数,虽然功能一样但是地址不同,这时需要使用useMemo。
特点
-
第一个参数是()=>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
想要记录n变化了多少次,那么就可以利用useRef。
forwardRef
我们希望能使用buttonRef能引用到button2对应的DOM对象,但是这样会报错,警告函数组件不能接受ref,报错内容会提示使用forwardRef
可以看到,由于props不包含ref,所以需要forwardRef
使用forwarfRef将button2包装起来起名button3,可以把外边的ref转发给button3第二个参数。再return时传给button
但是大部分情况下props不需要包含ref
自定义Hook
封装数据操作
自定义一个函数useList,并且把读写接口return出去,这样外面的组件就可以知道list的变化。
除了读写接口还可以加入增删的借口供给外部调用
还可以在自定义hook里面使用Context
Stale Closure
过时闭包