-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
源码分析:react hook 最佳实践 #18
Comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
源码分析:react hook 最佳实践
原文链接
前言
本文从
mini React
——Preact
源码的角度,分析React Hook
各个API
的优点缺点。从而理解为什么要用
hook
,以及如何最佳使用。2条规则
为什么?
源码分析
hook api
时,索引currentIndex + 1
依次存入数组。当组件
render
之前,会先调用hook render
,重置索引和设置当前组件,hook 注入在options
内。首先需要知道一点的是,
函数组件
在每次diff
时,整个函数都会重新执行,而
class组件
只会执行this.render
,因此hook
在性能上会有些损耗,考虑到这一点
hook
为那些声明开销很大的数据结构和函数,提供了useMemo
和useCallback
优化。hook
在每次render
时,取上一次hook state
时,如果在循环,条件或嵌套函数不确定的分支里执行,就有可能取错数据,导致混乱。
a = 1, b = 2, c = 3
;a = 1, c = 2
。c
取错了状态!第二条嘛,就显而易见了,
hook
寄生于react
组件和生命周期。Preact hook
在options
对象上声明了_render
->diffed
->_commit
->unmount
四个钩子,分别会在对象组件的生命周期前执行,这样侵入性较小。
useState
使用方式
state
值开销很大,可以传入函数,初始化只会执行一次。Object.is
判断),不会触发组件更新。为什么?
props.state === 1
初始化hook
,为什么props.state === 2
时,hook state
不会变化?hook state
变更是怎么驱动组件渲染的,为什么说可以当class state
使用?源码分析
Preact
中useState
是使用useReducer
实现的,便于行文,代码会略加修改。useState
只会在组件首次render
时初始化一次,以后由返回的函数来更新状态。props
的值来初始化useState
;hookState._value[0] !== nextValue
比较新旧值避免不必要的渲染。this.setState
函数。这就是为什么hook
可以代替class
的this.state
使用。useEffect
使用方式
query
参数,首次加载组件只发一次请求内容。useState
有说到,props
初始state
有坑,可以用useEffect
实现。useEffect
在使用state
时最好把它作为依赖,不然容易产生bug
当你点击按钮
a+=1
时,此时console.log
依旧打印0
。这是因为
useEffect
的副作用只会在组件首次加载时入_pendingEffects
数组,形成闭包。修改如下:
这段代码在
React
里运行,输出会随点击按钮而变化,而在preact
中,之前定时器未被清除,说明有
bug
。-_-||为什么?
useEffect
解决了什么问题一般发送数据请求
componentDidMount
中,之后componentWillUnmount
在相关清理。这就导致相互无关的逻辑夹杂在
componentDidMount
,而对应的扫尾工作却分配在componentWillUnmount
中。有了
useEffect
,你可以把相互独立的逻辑写在不同的useEffect
中,他人担心维护时,也不用担心其他代码块里还有清理代码。每次
diff
函数组件会被当做class
组件的this.render
函数类似使用,整体会被执行,在主体里操作副作用是致命的。
useEffect
的机制?源码分析
undefined
或依赖项数组中一个值变动,则true
_pendingEffects
数组中维护,代码有两处执行我很怀疑,
options._render
的代码是从flushAfterPaintEffects
不假思索的拷过去。导致上面讲到的一个
bug
。afterPaint
利用requestAnimationFrame
或setTimeout
来达到以下目的与
componentDidMount
、componentDidUpdate
不同的是,在浏览器完成布局与绘制之后,传给 useEffect 的函数会延迟调用,不会在函数中执行阻塞浏览器更新屏幕的操作。(勘误:
React
中useEffect
能达到这效果,Preact
并没有实现)useMemo
使用方式
为什么?
useMemo
解决了什么问题上面反复强调了,函数组件体会被反复执行,如果进行大的开销的会吃性能。
所以
react
提供了useMemo
来缓存函数执行返回结果,useCallback
来缓存函数。源码分析
可以看出,只是把传入的函数根据依赖性执行了一遍把结果保存在内部的
hook state
中。记住,所有的
hook api
都一样,不要在没有传入state
作为依赖项的情况下,在副租用中使用
state
。useCallback
使用方式
为什么?
useCallback
解决了什么问题上面提到了,用来缓存函数的
上面说过,没有依赖的时,不使要用
width
,但可以使用setWidth
,函数是引用,闭包变量
setWidth
是同一个地址。源码分析
useMemo
的封装useRef
使用方式
time
值不是最新的。可以用
time
的初始值来传给useRef
,再来驱动time
的更新。useRef
生成一个对象currentTime = {current: 60}
,currentTime
对象在组件的整个生命周期内保持不变。但这样处理有点多此一举,
setTime
函数式更新不就好了嘛,current
可以用来替代interval
,这样外部也能取消倒计时。这样既能消灭
interval
变量的反复创建,也能让外部能够清理定时器interval.current
。为什么?
useRef
返回的对象在组件的整个生命周期内保持不变,怎么理解?current
属性?源码分析
useMemo
来实现,传入一个生成一个具有current
属性对象的函数,空数组依赖,所以在整个生命周期该函数只执行一次。
useRef
返回的值,无法改变内部hookState._value
值,只能通过 改变内部hookState._value.current
来影响下次的使用。useLayoutEffect
使用方式
useEffect
使用方式相同。为什么?
useEffect
区别在哪里?源码分析
useEffect
的回调在option.diffed
阶段,使用
requestAnimationFrame
或setTimeout(callback, 100)
来异步执行,由于作者都认为this is not such a big deal
,所以代码就不贴了,而且只是有一层requestAnimationFrame
也达不到下一帧之前执行的效果。useLayoutEffect
的回调在option._commit
阶段批量同步处理。在
React
中估计使用了requestIdleCallback
或requestAnimationFrame
来进行时间分片,以避免阻塞视觉更新。由于
react
自己内部使用了优先级调度,势必会导致某些低优先级会延迟执行,只有你觉得优先级很高,在不管阻塞渲染的情况也要同步执行,那么你可以用
useLayoutEffect
。useReducer
使用方式
state
的初始值;state
的初始值。为什么?
useReducer
state 逻辑较复杂且包含多个子值,下一个 state 依赖于之前的 state。
使用
reducer
最好是个纯函数,集中处理逻辑,修改源头方便追溯,避免逻辑分散各处,也能避免不可预知的地方修改了状态,导致 bug 难追溯。源码分析
useState
是useReducer
实现的。state
为reducer
的第一个参数,dispatch
接受的参数为第二个参数,产生新的state
。useContext
使用方式
theme
useContext(MyContext)
相当于class 组件
中的static contextType = MyContext
当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。即使祖先使用 React.memo 或 shouldComponentUpdate,也会在组件本身使用 useContext 时重新渲染。
可以使用
React.memo
或useMemo hook
来性能优化。为什么?
源码分析
自定义 hook
使用方式
Select
或Input
组件,你可能分别对应使用他们来重新组合一个新的组件,把防抖实现在新组件内部。函数组件 hook 与 class 组件的对比
缺点
优势
class
和this
。class
目前还只是语法糖,标准还在更改,没有像传统面向对象的多态、多继承的概念,this
理解成本很高;ts
推导类型,等等。参考
The text was updated successfully, but these errors were encountered: