最简单的 useState 实现
useState 用法
点击button后,会执行+1
分析这一过程
在进行首次渲染时,会render <App/>,调用App(),得到虚拟DIV,并创建页面中的真实DIV
当用户点击button,会调用setN(n+1),再次render``<App/>,得到虚拟DIV,DOM Diff会更新真实DIV。
注意,每次调用APP(),都会运行useState(0),但是每次的n都不同了
几个问题
-
执行
setN时会发生什么?n会变吗?App()会重新执行吗?执行
setN会重新渲染UI,n不会改变,App()会重新执行 -
如果
App()会重新执行,那么useState(0)的时候。n每次的值会有不同吗
分析
-
setN
setN一定会修改数据state,将n+1存入statesetN一定会触发<App/>重新渲染(re-render) -
useState
useState肯定会从state读取n的最新值 -
state
每个组件都有自己的数据
state
尝试实现 React.useState
在myUseState外面声明一个不会被其重置的变量_state
改进
如果一个组件用了两个 useState怎么办?
由于所有数据都放在_state,会产生冲突
改进思路如下:
-
把
_state做成一个对象比如
_state={n:0,m:0}行不通,因为
useState(0)并不知道变量叫n还是m -
把
_state做成数组比如
_state=[0,0]可行,代码
_state数组方案缺点
useState调用顺序——
若第一次渲染时,n是第一个,m是第二个,k是第三个
则第二次渲染时必须保证顺序完全一致
所以React不允许出现如下代码
其他问题
-
APP用了
_state和index,那其他组件用什么?给每个组件创建一个
_state和index -
那么放在全局作用域里重名怎么办?
放在组件对应的虚拟节点对象上
图示
总结
每个函数组件对应一个React节点,每个节点保存着state和index,useState会读取state[index],index由useState出现的顺序决定,setState会修改state,并触发更新。
注意:这里是对React的简化,React节点应该是FiberNode,_state的真实名称为memorizedState,index的实现则用到了链表。
推荐文章阅读源码后,来讲讲React Hooks是怎么实现的
useRef
两种操作——
- 点击
+1,再点击log——无bug,打印出n:1 - 点击
log,再点击+1——有bug,打印出n:0
所以为什么log出了旧的n呢?
贯穿始终的状态
实现贯穿始终的状态,有很多方法
全局变量
用window.xxx即可
useRef
useRef不仅可以用于div,还能用于任意数据
注意:这里不会出bug,但是{nRef.current}不能实时更新
强制更新的例子,不推荐使用
useContext
useContext不仅能贯穿始终,还能贯穿不同组件