30行代码从零实现useState和useEffect
2024/07/28
本文完整示例代码在:codesandbox.io
实现一个最基础的useState
让我们从最简单的useState实现开始:
上线代码虽然可以实现state的初始化和更新,但是不足是:我们使用的是一个函数来获取state的,而React里面使用的是变量获取的。
我们来一步步优化代码。
重构:模拟 React 的工作方式
useState使用React模块导出的,我们也来实现一下:
使用useState
React是在组件内使用useState,组件如下:
Component的render方法就是打印当前a变量,而click则会重新设置a
添加render方法
我们需要在React模块内增加一个render方法来渲染上面的Component:
接下来执行上面的代码,可以看到每一次点击,我们都会重新渲染组件,得到正确的state值
不过现在上面这个实现不支持多个state,我们继续完善
支持多个state
为了支持多个 state,我们需要对实现进行进一步改进。
完整实现如下:
这里有一些关键点需要注意:
- useState里面的index不能直接使用index,因为当你调用setState时index已经变化了,下面是错误的示范
- 每次调用React.render的时候,需要重置index,重新渲染的时候才能获取到最新的state
hooks可以用在条件语句吗?
面试的时候我们经常会遇到这个问题,结合我们上面的实现可以看出,Hooks 不能在条件语句中使用。这是因为:
- Hooks 的工作原理依赖于它们被调用的顺序。
- 在我们的实现中,我们使用了一个全局的
index
来跟踪当前正在处理的 Hook。每次调用 Hook 时,index
都会递增。
- 如果我们在条件语句中使用 Hook,那么在某些渲染中,这个 Hook 可能不会被调用,这会导致
index
的不一致。
- 不一致的
index
会导致 Hooks 的状态混乱,可能会把一个 Hook 的状态赋给另一个 Hook。
举个例子:
在这个例子中,当 count <= 5
时,name
这个 state 不会被创建。但是当 count > 5
时,突然多了一个 state。这会导致后面的 Hooks 的 index
发生错位,从而引起错误。
实现useEffect
让我们来实现 useEffect
Hook。useEffect
用于处理副作用,它接受两个参数:一个回调函数和一个依赖数组。
useEffect
实现的核心思路是:我们需要吧effect的依赖存在hooks数组,里面然后去对比新旧依赖
useEffect
函数首先检查是否存在旧的依赖数组。
- 如果存在旧的依赖数组,它会比较新旧依赖是否有变化。我们使用数组
some
方法进行比较
- 如果依赖发生了变化(或者是第一次运行),就执行回调函数。
- 最后,保存新的依赖数组,并增加 index。
代码如下:
控制台的输出顺序如下:第一行先执行了副作用也就是console,当我们updateCount
之后,又会执行一次副作用的console
在effect的依赖树组里面再加一个fruit
变量,也可以正常运行
完。