最简单的 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
存入state
setN
一定会触发<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
不仅能贯穿始终,还能贯穿不同组件