简单的 useState 实现

本文写于 2020 年 10 月 21 日

以下是一段非常简单的 React 代码:

const App = () => {
const [n, setN] = useState(0);
return (
<div>
{n}
<button onClick={() => setN(x => x + 1)}>+1</button>
</div>
);
} React.render(<App />, rootElement)

这样的用法和以往的 setState 是有明显的不同的,他看起来更像 redux——我们初始化一个 state,然后 dispatch 一个 action,再由 reducer 改变 state 后返回新的 state

Redux 思想实现 useState

既然我们觉得它像,那我们就来自己实现一个吧。

不熟悉 Redux 思想的同学请自行阅读文档

const useState = (initialValue) => {
let state = initialValue;
const dispatch = (newState) => {
state = newState;
render(<App />, document.getElementById('root'));
};
return [state, dispatch];
};

然后我们用这个自定义的 useState 代替 React 的 useState——就会发现我们失败了,setN 无论如何都不会有任何反应。

这是因为我们每次重新 render 的时候都重新执行了函数,于是我们总是会重新赋值

为什么不会重新赋值?

对于这段 React 代码来说,当我们第一次运行时,React 会进行首次渲染,即 render(<App />, ...)

在此过程中,会先调用 App(),之后便会得到虚拟 DOM,再创建真实的 Div。

当我们触发点击事件时,会调用 setN,再次 render()。之后调用 App(),然后得到新的虚拟 DOM,进行 diff 算法,根据 diff 算法的结果去更新新的 Div。

而不管是第一次渲染,还是第二次调用,都会调用 useState()

但是我们写的是 useState(0) 啊,两次调用明明是一样的代码,为何 n 的值不同?怎么解决这个问题呢?

很简单,闭包嘛。

const createUseState = () => {
let state;
const useState = (initialValue) => {
if (!state) {
state = initialValue;
}
const dispatch = (newState) => {
state = newState;
render(<App />, document.getElementById('root'));
};
return [state, dispatch];
};
};

这样就解决了重新赋值的问题。

多次调用

但是我们需要多次调用 useState 呀,不可能只用一次的。

于是我们将 state 改为一个数组:const state = [];

const createUseState = () => {
const state = [];
let index = 0;
return (initialValue) => {
state[index] = state[index] || initialValue;
const currentIndex = index;
const dispatch = (newState) => {
state[currentIndex] = newState;
// 重点
index = 0;
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
};
return [state[index++], dispatch];
};
};

我们创建了一个 index 变量来控制索引。它需要我们保证每次重新渲染 App 传入数组的元素是一样的——这就是为什么我们不可以将 useState 写在 if 判断中。

在上述代码中有一处重点,在于我们需要在每次 set 之后将索引归零 index = 0

因为每次 render 结束后,React 都会重新执行该函数。

(完)

最新文章

  1. Android 自定义 attr
  2. Scrum Meeting 20161209
  3. nyoj 925 国王的烦恼(最小生成树)
  4. Swift开发第二篇——extension及fatalError
  5. TCP之心跳包实现思路
  6. 【转】appium_python_API文档
  7. Android-----第三方 ImageLoader 的简单配置和使用
  8. C的输入输出函数的基本用法
  9. hdu 4939 Stupid Tower Defense ( dp )
  10. ALEXANDER WANG 北京旗舰店开业活动
  11. .NET中IDisposable接口的基本使用
  12. 流API--使用并行流
  13. python 之协程
  14. JavaScript之优化DOM
  15. vue 使用 supermap iclient-classic
  16. auth mysql
  17. my first note
  18. 自己搭建git 代码服务器
  19. 拟物设计和Angular的实现 - Material Design
  20. mysql数据库,创建只读用户

热门文章

  1. Spring MVC 框架有什么用?
  2. 【leetcode 29】 两数相除(中等)
  3. 遇到的问题之“解决tomcat中文乱码问题”
  4. django REST框架- Django-ninja
  5. Python模块导入方式
  6. 电源PCB布板的10个基本法则
  7. JavaScript 工作原理之七-Web Workers 分类及 5 个使用场景
  8. 【uniapp 开发】文字缩略css
  9. datetimepicker 设置日期格式、初始化
  10. Mysql集群搭建-实操