等一下...React.useState 是如何工作的?
React Hooks 已经发布了一段时间了,它真的很棒!我已经在生产代码中使用过它们了,它让一切看起来都更美观了。随着我不断使用 Hooks,我开始好奇它究竟是如何运作的。
显然我不是唯一一个,因为波士顿有一个关于这个话题的 React 聚会。非常感谢 Ryan Florence 和 Michael Jackson(不是 Moonwalking 的传奇人物)就这个主题发表了如此精彩的演讲。继续观看,你将学到更多关于它的知识useEffect
以及它的工作原理!
它是如何工作的?
您创建一个功能组件并向其抛出一些可跟踪状态的 React hook,还可以更新它,然后它就可以工作了。
我们中的许多人以前都见过这个例子的一些变体:
一useState
import React from "react";
const App = () => {
const [count, setCount] = React.useState(1);
return (
<div className="App">
<h1>The infamous counter example</h1>
<button onClick={() => setCount(count - 1)}>-</button>
<span style={{ margin: "0 16px" }}>{count}</span>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
};
export default App;
好的,很棒,但它是如何实现这种魔力的呢?看看这一React.useState
行代码。它读起来非常简单,我从未质疑过它。我有一个析构数组,它提取count
值,然后调用某个函数setCount
,它会用我传入的默认值初始化 count useState
。当我React.useState
向图片中添加另一个时会发生什么?
二useState
,哈哈哈
有谁是德古拉伯爵吗?
const App = () => {
const [count, setCount] = React.useState(1);
const [message, setMessage] = React.useState("");
const adder = () => {
if (count < 10) {
setCount(count + 1);
setMessage(null);
} else {
setMessage("You can't go higher than 10");
}
}
const subtracter = () => {
if (count > 1) {
setCount(count - 1);
setMessage(null);
} else {
setMessage("You can't go lower than 1, you crazy");
}
}
return (
<div className="App">
<h1>The infamous counter example</h1>
<button onClick={subtracter}>-</button>
<span style={{ margin: "0 16px" }}>{count}</span>
<button onClick={adder}>+</button>
<p>{message}</p>
</div>
);
};
现在,每当用户试图超出 1 - 10 的范围时,我们都会显示一条消息
在我们的组件中,我们有两个解构数组,它们使用同一个React.useState
钩子,但默认值不同。哇,现在我们开始探索它的奥妙了。
好吧,让我们删除它React
,React.useState
我们应该得到一个引用错误,说“useState 未定义”
让我们实现我们自己的useState
功能。
逆向工程useState
函数
函数useState
具有一个值和一个设置该值的函数
像这样:
const useState = (value) => {
const state = [value, setValue]
return state
}
我们仍然会得到referenceErrors,因为我们还没有定义setValue
。我们知道setValue是一个函数,因为我们在useState
count中是如何使用它的useState
:const [count, setCount] = React.useState(1);
致电setCount
:setCount(count + 1);
创建该setValue
函数不会再导致错误,但-
和+
按钮不起作用。
const useState = (value) => {
const setValue = () => {
// What do we do in here?
}
const state = [value, setValue]
return state
}
如果我们尝试更改默认值,useState
它会更新count
👍🏽。至少有些功能还在运行😂。
继续弄清楚到底setValue
是干什么的。
我们看到setCount
它正在执行某种类型的值重新赋值,然后导致 React 重新渲染。这就是我们接下来要做的。
const setValue = () => {
// What do we do in here?
// Do some assigning
// Rerender React
}
我们将向我们的函数传递一个新的值参数setValue
。
const setValue = (newValue) => {
// What do we do in here?
// Do some assigning
// Rerender React
}
newValue
但是我们在函数内部做什么呢setValue
?
const setValue = (newValue) => {
// Do some assigning
value = newValue // Does this work?
// Rerender React
}
value = newValue
有道理,但这并没有更新计数器的值。为什么?当我console.log
在里面setValue
和外面的时候setValue
,我们看到的就是这样的。
刷新页面后,计数初始化为 1,消息初始化为 null,一切正常。点击按钮+
后,计数值增加到 2,但屏幕上并没有更新计数。🤔 也许我需要手动重新渲染浏览器来更新计数?
实现一种手动重新渲染浏览器的糟糕方式
const useState = (value) => {
const setValue = (newValue) => {
value = newValue;
manualRerender();
};
const state = [value, setValue];
return state;
};
.
.
.
const manualRerender = () => {
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
};
manualRerender();
浏览器中的计数仍然没有更新。怎么回事?
我卡在这上面一段时间了,现在知道原因了。console.log
创建完成后就说明吧。
const state = [value, setValue];
console.log(state)
我们的调用useState
引发了第一次渲染,我们得到:[1, setValue()]
在我们第二次调用时,useState
我们渲染:[null, setValue()]
为了更好地将其形象化,让我们添加一个渲染跟踪器来计算渲染屏幕的次数。
let render = -1
const useState = (value) => {
const setValue = (newValue) => {
value = newValue;
manualRerender();
};
const state = [value, setValue];
console.log(++render)
console.log(state)
return state;
};
我们的函数如何setValue
知道要更新哪个值?它不知道,因此我们需要一种方法来跟踪它。您可以使用数组或对象来实现这一点。我选择了红色药丸类型的对象。
在函数之外useState
,我们将创建一个名为states
const states = {}
在函数内useState
初始化states
对象。我们使用括号来分配键/值对。
states[++render] = state
我还将创建另一个变量,用于id
存储渲染值,以便我们可以取出++render
括号内的。
你应该得到类似这样的结果:
let render = -1;
const states = {};
const useState = (value) => {
const id = ++render;
const setValue = (newValue) => {
value = newValue;
manualRerender();
};
const state = [value, setValue];
states[id] = state;
console.log(states);
return state;
};
我们的物体是什么states
样子的?
states = {
0: [1, setValue],
1: [null, setValue]
}
所以现在我们点击加减按钮时……又什么也没有了。哦,对了,因为value = newValue
仍然没有任何反应。
但是确实发生了一些事情。如果你查看控制台,你会发现每次我们点击其中一个按钮时,它都会不断地将相同的数组添加到我们的states
对象中,但count
不会递增,并且消息仍然为空。
所以setValue
需要去寻找value
,然后将其分配newValue
给value
。
const setValue = (newValue) => {
states[id][0] = newValue;
manualRerender();
};
然后我们要确保我们只更新键:0 和 1,因为它们将是我们的两个useState
位置。
因此,转到该manualRerender
函数并添加对它的调用render
并将其重新分配给 -1
const manualRerender = () => {
render = -1;
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
};
我们这样做是因为每次我们调用 setValue 时它都会调用manualRerender
函数设置render
回 -1
最后,我们将添加一个检查来判断对象是否存在。如果存在,我们就返回该对象。
if (states[id]) return states[id];
呼。这可真是费劲,而且这只是一个非常简单的实现方法useState
。幕后还有很多事情要做,但至少我们对它的工作原理有了大致的了解,并且稍微揭开了它的神秘面纱。
查看所有代码并尝试在脑海中构建一个模型来了解它的工作原理。
希望这有帮助😊
鏂囩珷鏉ユ簮锛�https://dev.to/rembrandtreyes/wait-how-does-react-usestate-work-44np