React:Hooks原理

341 阅读2分钟

最简单的 useState 实现

useState 用法

示例

点击button后,会执行+1

image-20210729235317879.png

分析这一过程

在进行首次渲染时,会render <App/>,调用App(),得到虚拟DIV,并创建页面中的真实DIV

当用户点击button,会调用setN(n+1),再次render``<App/>,得到虚拟DIVDOM Diff会更新真实DIV

注意,每次调用APP(),都会运行useState(0),但是每次的n都不同了

79.1.1.gif

几个问题

  • 执行setN时会发生什么?n会变吗?App()会重新执行吗?

    执行setN会重新渲染UIn不会改变,App()会重新执行

  • 如果App()会重新执行,那么useState(0)的时候。n每次的值会有不同吗

分析

  • setN

    setN一定会修改数据state,将n+1存入state

    setN一定会触发<App/>重新渲染(re-render)

  • useState

    useState肯定会从state读取n的最新值

  • state

    每个组件都有自己的数据state

尝试实现 React.useState

点击查看代码

myUseState外面声明一个不会被其重置的变量_state

79.1.2.gif

改进

如果一个组件用了两个 useState怎么办?

由于所有数据都放在_state,会产生冲突

改进思路如下:

  • _state做成一个对象

    比如_state={n:0,m:0}

    行不通,因为useState(0)并不知道变量叫n还是m

  • _state做成数组

    比如_state=[0,0]

    可行,代码

_state数组方案缺点

useState调用顺序——

若第一次渲染时,n是第一个,m是第二个,k是第三个

则第二次渲染时必须保证顺序完全一致

所以React不允许出现如下代码

image-20210730231123188.png

image-20210730231312484.png

其他问题

  • APP用了_stateindex,那其他组件用什么?

    给每个组件创建一个_stateindex

  • 那么放在全局作用域里重名怎么办?

    放在组件对应的虚拟节点对象上

图示

image-20210730231607111.png

总结

每个函数组件对应一个React节点,每个节点保存着stateindexuseState会读取state[index]indexuseState出现的顺序决定,setState会修改state,并触发更新。

注意:这里是对React的简化,React节点应该是FiberNode_state的真实名称为memorizedStateindex的实现则用到了链表。

推荐文章阅读源码后,来讲讲React Hooks是怎么实现的

useRef

示例

两种操作——

  1. 点击+1,再点击log——无bug,打印出n:1
  2. 点击log,再点击+1——有bug,打印出n:0

所以为什么log出了旧的n呢?

image-20210730235818555.png

贯穿始终的状态

实现贯穿始终的状态,有很多方法

全局变量

window.xxx即可

useRef

useRef不仅可以用于div,还能用于任意数据

useRef

79.4.1.gif

注意:这里不会出bug,但是{nRef.current}不能实时更新

强制更新的例子,不推荐使用

useContext

useContext不仅能贯穿始终,还能贯穿不同组件

useContext例子