React Hooks 详解
useState
- 使用状态
const[n,setN] = React.useState(0)
`const[user,setUser] = React.useState({name:'F'}) - 注意事项1: 不可局部更新
如果state是一个对象,能否部分setState?
答案是不行的 示例代码
因为setState不会帮我们合并属性
那么useReducer会合并属性吗? 也不会 - 注意事项2: 地址要变
setState(obj)如果obj地址不变,那么React就认为数据没有变化 - useState接受函数
const [state,setState] = useState(()=>{
return initialState
})
//该函数返回初始state,且只执行一次
- setState接受函数
setN(i=>i+1)
什么时候用这种方式? 看代码
如果你能接受这种形式,应该优先使用这种形式
useReducer
- 用来践行Flux/Redux的思想
看代码,共分四步走
一、创建初始值initialState
二、创建所有操作reducer(state,action)
三、传给useReducer,得到读和写API
四、调用写({type:'操作类型'})
总的来说useReducer是useState的复杂版
示例代码 - 一个用useReducer的表单例子
链接
如何代替Redux
- 步骤
一、将数据集中在一个store对象
二、将所有操作集中在reducer
三、创建一个Context
四、创建对数据的读写API
五、将第四步的内容放到第三步的Context
六、用Context.Provider将Context提供给所有组件
七、各个组件用useContext获取读写API - 例子
代码
如何模块化?其实很简单,代码
useContext
- 上下文
全局变量是全局的上下文
上下文是局部的全局变量 - 使用方法
之前的例子已经介绍过了
一、使用C=createContext(initial)创建上下文
二、使用<C.provider>圈定作用域
三、在作用域内使用useContext(C)来使用上下文
代码示例 - 不是响应式的
你在一个模块将C里面的值改变
另一个模块不会感知到这个变化
useEffect
- 副作用(API名字叫得不好)
对环境的改变即为副作用,如修改document.title
但我们不一定非要把副作用放在useEffect里
实际上叫做afterRender更好,每次render后运行 - 用途
作为componentDidMount使用,[]作第二个参数
作为componentDidUpdate使用,可指定依赖
作为componentWillUnmount使用,通过return
以上三种用途可同时存在 - 特点
如果同时存在多个useEffect,会按照出现次序执行
useLayoutEffect
- 布局副作用(听起来很...)
useEffect在浏览器渲染完成后执行
例子
useLayoutEffec在浏览器渲染前执行
通过时间点来侧面证明(此处要画图理解) - 特点
useLayoutEffect总是比useEffect先执行
useLayoutEffect里的任务最好影响了Layout - 经验
为了用户体验,优先使用useEffect (优先渲染)
useMemo
-
要理解React.useMemo
需要先讲React.memo
React默认有多余的render
讲代码中的Child用React.memo(Child)代替
如果props不变,就没有必要再次执行一个函数组件
最终效果:代码 -
但是
这玩意有一个bug:代码
添加了监听函数之后,一秒破功
因为App运行时会在次执行第12行,生成新的函数
新旧函数虽然功能一一样,但是地址不一样!
怎么办?用useMemo:代码 -
特点
第一个参数是()=> value,见定义
第二个参数是依赖[m,n]
只有当依赖变化时,才会计算出新的value
如果依赖不变,那么就重用之前的value
这不就是Vue 2的computed吗? -
注意
如果你的value是个函数,那么你就要写成useMemo(()=> (x) :=> console.1og(x))
这是一个返回函数的函数
是不是很难用?于是就有了useCallback
useCallback
- 用法
useCallback(x => log(x),[m])等价于useMemo(() => x => log(x), [m])
useRef
-
目的
如果你需要一个值,在组件不断render时保持不变
初始化: const count = useRef(0)
读取:count.current
为什么需要current?
为了保证两次useRef是同一个值(只有引用能做到)
此处需要画图解释 -
延伸
看看Vue 3的ref
初始化: const count = ref(0)
读取: count.value
不同点:当count.value变化时,Vue 3会自动render -
能做到变化时自动render吗?
不能!
为什么不能?因为这不符合React的理念
React的理念是UI = f(data)
你如果想要这个功能,完全可以自己加
监听ref,当ref.current变化时,调用setX即可 -
不想自己加?
那你就用Vue3吧,Vue3帮你加好了
forwardRef
- 讲了useRef就不得不讲一下它了
代码1: props 无法传递ref属性
代码2:实现ref的传递
代码3:两次ref传递得到button的引用 - useRef
可以用来引用DOM对象
也可以用来引用普通对象 - forwardRef
由于props不包含ref,所以需要forwardRef
为什么props不包含ref呢?因为大部分时候不需要
useIMperativeHandle
- 看起来很复杂的名字
应该叫做setRef
不用useImperativeHandle的代码
用了useImperativeHandle的代码 - 分析
用于自定义ref的属性
自定义 Hook
Stale Closure
- 过时闭包
参考文章链接
本文为fjl的原创文章,著作权归本人和饥人谷所有,转载务必注明来源