动机
使用 hooks 的动机:
- 在组件之间复用状态逻辑很难;
class 组件要实现复用逻辑,通常会以高阶组件的形式实现或render props,但它们需要重新组织你的组件结构并让组件嵌套变深,代码难以理解,形成“嵌套地狱”。
- 复杂组件变得难以理解;
当一个简单的组件,由于需要的迭代,组件间的状态散布到生命周期的各个时期里,而状态之间既互相关联又互相不影响,导致代码拆分困难,组件的逻辑与代码越来越庞大,变得难以阅读理解。
- class 让开发人员与计算机都难理解,而表现为函数式编程比 OOP 更加简单;
class 你必须去理解 JavaScript 中 this 的工作方式,这与其他语言存在巨大差异。还不能忘记绑定事件处理器,当然可以使用箭头函数解决。
class 不能很好的压缩,并且会使热重载出现不稳定的情况。
useReducer
useState 其实就是阉割版的 useReducer,但在实际开发中 useState 才是主要被使用的,而 useReducer 是 useState 的替代方案。
在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。
比如直接在函数组件里调用请求之类的,需要等请求的数据回来后才进行某些操作,那么你就要维护一个 loading 与 data 的 state,而异步的 state 是 useState 后就立刻 render 一次,那么执行两次 useState,就会执行两次的 render,这就有可能出现一些执行顺序上的 BUG 问题,所以我们可以使用 useReducer 把它们维护到一个 state 上,并且 useReducer 里的 reducer 可以执行一些逻辑上的操作。
如:
1 | import { useReducer, useEffect } from "react"; |
问题
hooks 接入 React-Redux 后 mapDispatchToProps 的第二参数是有问题的,如果使用了第二参数,那么每当 connected 的组件接收到新的 props 时,mapDispatchTopProps 都会被调用,这意味着组件会返回一个新的 props,导致使用 useEffect 监听 props 的属性的都会被触发掉而出现一些 BUG 问题,当然如果有正确判断规则,那么这个对于项目来说问题不大,就是多了一次 render 机会,不会严重影响程序与用户体验。
源码浅析
- React 通过单链表来管理 Hooks
- 按 Hooks 的执行顺序依次将 Hook 节点添加到链表中
- 每个 Hook 节点通过循环链表记住所有的更新操作
- 在 update 阶段会依次执行 update 循环链表中的所有更新操作,最终拿到最新的 state 返回
- FiberNdoe 节点中会又一个 updateQueue 链表来存放所有的本次渲染需要执行的 effect。
- mountEffect 阶段和 updateEffect 阶段会把 effect 挂载到 updateQueue 上。
- updateEffect 阶段,deps 没有改变的 effect 会被打上 NoHookEffect tag,commit 阶段会跳过该 Effect。
为什么只能在函数顶层使用 Hooks 而不能在条件语句等里面使用 Hooks?
其实 hooks 就是把挂载与更新分开来了,如果组件第一次加载那么就会走 mount 的函数,更新后走 update 的函数,由于函数组件其实就是 render 函数,所以每次的更新都会把整个函数都重新执行一遍,那么执行的函数里的钩子就要与第一次执行时是一致,这是为什么,你可以想象一下,数组,数组只有下标,而每次执行函数后函数里的数组都是重新 push 一遍内容的,那么如果组件第一次加载时保存一个全局钩子数组,与这次的钩子的 push 进去的不一致,那么全局钩子与更新的钩子匹配时整个更新就会错乱。
而像 preact 模拟 hooks 直接就使用数组,没有使用链表结构,然后每次获取当前 hooks 钩子都是 index 索引加一方式。
useState 的实现与 useReducer 是一样的,因为 useState 返回的就是一个 state 与 dispatch,只是它的 action 是内容实现的,action 就是更新的内容,不需要有 type 这样的字段。
资料
React Hooks 源码解析(3):useState
React Hooks 源码模拟与解读
React Hooks 源码解析,原来这么简单~