简单的 useState 实现
简单的 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 都会重新执行该函数。
(完)
简单的 useState 实现的更多相关文章
- Hooks中的useState
Hooks中的useState React的数据是自顶向下单向流动的,即从父组件到子组件中,组件的数据存储在props和state中,实际上在任何应用中,数据都是必不可少的,我们需要直接的改变页面上一 ...
- 手写useState与useEffect
手写useState与useEffect useState与useEffect是驱动React hooks运行的基础,useState用于管理状态,useEffect用以处理副作用,通过手写简单的us ...
- useContext 让父子组件传值更简单(五)
有了useState和useEffect已经可以实现大部分的业务逻辑了,但是React Hooks中还是有很多好用的Hooks函数的,比如useContext和useReducer. 在用类声明组件时 ...
- useState 的介绍和多状态声明(二)
useState的介绍 useState是react自带的一个hook函数,它的作用是用来声明状态变量. 那我们从三个方面来看useState的用法,分别是声明.读取.使用(修改).这三个方面掌握了, ...
- React Hooks简单业务场景实战(非源码解读)
前言 React Hooks 是React 16.7.0-alpha 版本推出的新特性.从 16.8.0 开始,React更稳定的支持了这一新特性. 它可以让你在不编写 class 的情况下使用 st ...
- 前端开发:mock.js的简单应用(生成随机数据,拦截 Ajax 请求)
摘要 在前端开发过程中,后端接口还没有完全开发完成时,前端开发人员就需要学会自己模拟后端接口数据,更快更好的完成开发任务.模拟后端接口数据的js库有很多,今天就简单就简单的分享下mock.js在前端开 ...
- 认清 React 的useState逻辑
useState运行过程解析 function App() { const [n, setN] = useState(0); //使用 myUseState() return ( <div> ...
- React简单教程-6-单元测试
前言 我想大部分人的前端测试,都是运行项目,直接在浏览器上操作,看看功能正不正常.虽然明明有测试库可以使用,但是因为"要快"的原因,让好好做测试变成了一件影响效率的事. 因为这种无 ...
- React简单教程-5-使用mock
前言 一个前后端分离的项目,前端人员需要对接后端的接口.如果在后端的接口没有开发好,或者没有测试版可以对接的情况下,前端人员也不能坐等后端接口写好后再开始开发. 一个项目的,理想情况下接口的规范应该是 ...
随机推荐
- Java 线程数过多会造成什么异常?
1.线程的生命周期开销非常高 2.消耗过多的 CPU 资源 如果可运行的线程数量多于可用处理器的数量,那么有线程将会被闲置.大量空 闲的线程会占用许多内存,给垃圾回收器带来压力,而且大量的线程在竞争 ...
- 构造器constructor是否可被重写override?
构造器不能被继承,因此不能被重写,但可以被重载.
- Numpy的数学统计函数
Numpy的数学统计函数 本节内容: 1.Numpy有哪些数学统计函数: 函数名 说明 np.sum 所有元素的和 np.prod 所有元素的乘积 np.cumsum 元素的累积加和 np.cumpr ...
- element el-tree、el-table组件加载数据前闪现 暂无数据 清除
相信很多人在使用element el-tree.el-table组件加载数据前会显示一个" 暂无数据 ",体验很不友好,有没有办法处理不显示呢?答案是:有的.废话不多说直接上代码 ...
- React+dva+webpack+antd-mobile 实战分享(二)
第一篇 https://segmentfault.com/a/11... 在上一篇文章中教给大家了怎么搭建项目的架子:那么今天我们就来说一下项目里的导航和列表的实现 导航 废话不说啦 下面直接给大家讲 ...
- python-使用函数求余弦函数的近似值
本题要求实现一个函数,用下列公式求cos(x)近似值,精确到最后一项的绝对值小于eps(绝对值小于eps的项不要加): cos(x)=0!x0−2!x2+4!x4−6!x6+... 函数接口定 ...
- hdfs对文件的增删改查
源代码: pom.xml: <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&qu ...
- ubantu系统之 在当前文件夹打开终端
直接安装一个软件包 "nautilus-open-terminal"终端输入:sudo apt-get install nautilus-open-terminal重启系统!
- oracle 11g手工建数据库过程
创建需求和目的 1.需要把11机器上的oracle库中的表导入到12机器oracle库 2.11机器上的数据库fp在12机器上不存在,需要创建新库,以便导入.3.我们只需导入业务库shp即可,不需要把 ...
- SprigCloud入门踩坑之创建bean失败
昨天也是报同样的错误,恰好要去吃饭,着急就没找到问题,把项目删了,后续看了路飞大佬的通过pom导入依赖的方式,但是资源导入太慢随放弃. 昨晚熄灯前二十分钟又从头敲了一遍,敲好就断电了,没来得及启动,今 ...