假设我们有一个简单的功能组件:
export function MyComp(){
const [state, setState] = React.useState({foo:1});
setTimeout(() => setState({foo:2}, 45);
return (
<div> {state.foo} </div>
)
}
如果setState
没有再次神奇地调用MyComp()
,它将如何重新渲染?如果它神奇地再次调用MyComp()
,这是怎么回事?
在代码中,setState
不会神奇地调用组件,而是触发重新渲染。因此,在您的情况下执行setState(someValue)
是导致MyComp
重新渲染的原因。您可以在React文档中阅读更多关于这一点和钩子的信息:https://reactjs.org/docs/hooks-state.html#recap
希望它能回答你的问题!
我不是React机制本身的专家,但在使用一些CS概念的非常简化的版本中,它背后的想法可能如下:
首先,React必须有一个应用程序中所有组件的列表,我们将用列表来简化它,但实际上它是一棵树:
const nodes = <>
<h1>My Counter</h1>
<Counter />
<div>
</>
因此,在这一点上,如果您的应用程序是平面的,DummyReactDOM.render(nodes)
将创建这些节点的列表":
nodes = [ h1, Counter, div ]
当调用useState
时会变得棘手,React将要做的是保存某种状态(记忆状态(,例如Counter
节点可能看起来像这样:
function Counter() {
[ counter, setCounter ] = useState(0);
}
这将为节点添加一个状态:
node Counter = {
state : {
counter: 0
next : null
}
}
现在,当我们点击组件并调用setCounter(counter + 1)
时会发生什么?将下一个状态添加到队列中:
node Counter = {
state : {
counter : 0,
next : {
state : {
counter : 1, // <-- setCounter(counter + 1 happens)
next : null,
}
}
}
}
// firstState.next -> secondState.next -> null
现在,调度器是了解哪些组件需要重新渲染的主要思想。以下是一种非常不现实但易于理解的看待方式:
while (true) {
// sleepForABit();
if (currentNode.state.next !== null && currentNode.state !== currentNode.state.next) {
currentNode.state = currentNode.state.next;
currentNode.component.render(); // or functionalComponent() call
}
currentNode = currentNode.next
}
如果它是一个节点列表,这是一个简单的情况,但很明显,由于节点需要重新渲染,React将检查其子节点。需要注意的是,重新渲染实际上并没有那么昂贵,昂贵的部分是重新创建DOM节点(以及在较小程度上更新它们(,因此检查子节点的性能大多相当高。
希望这能有所帮助!
如果您想知道为什么我们可以在不总是使用counter = 0
的情况下执行setCounter(counter + 1)
,React保证在某些事件上重新渲染节点(onClick
是众多事件之一(。