lua*_*wtf 6 javascript reactjs
在 React 中,每次渲染/重新渲染组件时,它都会使用createElement. React 如何知道何时在重新渲染之间保持组件状态?
例如,请考虑以下代码:
class Timer extends Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
tick() {
this.setState(state => ({ seconds: state.seconds + 1 }));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return createElement('div', null,
'Seconds: ',
this.state.seconds
);
}
}
class Button extends Component {
constructor(props) {
super(props);
this.state = { clicks: 0 };
}
click() {
this.setState(state => ({ clicks: state.clicks + 1 }));
}
render() {
return createElement('button', { onClick: () => this.click() },
createElement(Timer, null),
'Clicks: ',
this.state.clicks
);
}
}
render(createElement(Button, null), document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)
您可以在此处使用 Preact REPL 尝试此代码。
请注意,当按下按钮并更新 clicks 值时,Timer组件的状态将保持不变且不会被替换。React 如何知道重用组件实例?
虽然乍一看这似乎是一个简单的问题,但当您考虑更改传递给子组件的 props 或子组件列表等内容时,它会变得更加复杂。React 如何处理更改子组件的 props?即使它的道具发生了变化,子组件的状态是否仍然存在?(在 Vue 中,组件的状态在它的 props 改变时会保持不变)列表怎么样?当子组件列表中间的条目被删除时会发生什么?对这样的列表进行更改显然会生成非常不同的 VDOM 节点,但组件的状态仍然存在。
createElementVS renderVS 坐骑当渲染像您这样的 React 组件时Button,会使用createElement. createElement(Timer, props, children)不会创建组件的实例,甚至不会渲染它,它只会创建一个“React 元素”,它表示应该渲染Timer组件的事实。
当你的Button渲染完成后,react 会将结果与之前的结果进行协调,以决定需要对每个子元素执行什么操作:
Button第一次渲染时,所有子项都将是新的(因为没有先前的结果可匹配)。如果 React 比较它们并且它们具有相同的类型,则一个元素“匹配”另一个元素。
React 比较子元素的默认方法是同时迭代两个子元素列表,将第一个元素相互比较,然后比较第二个元素,依此类推。
如果子级有keys,则将新列表中的每个子级与旧列表中具有相同键的子级进行比较。
有关更详细的说明,请参阅React Reconciliation Docs 。
您Button总是返回一个元素: a button。因此,当您Button重新渲染、button匹配以及重新使用其 DOM 元素时,就会button比较 的子元素。
第一个子级始终是 a Timer,因此类型匹配并且组件实例被重用。propsTimer没有改变,所以 React 可能会重新渲染它(调用render具有相同状态的实例),或者可能不会重新渲染它,从而使树的该部分保持不变。这两种情况都会在您的情况下产生相同的结果 - 因为您没有副作用render- 并且 React 故意将何时重新渲染的决定保留为实现细节。
第二个子元素始终是字符串"Clicks: ",因此 React 也会单独保留该 DOM 元素。
如果this.state.click自上次渲染以来发生了变化,那么第三个子节点将是一个不同的字符串,可能从 变为"0","1"因此文本节点将在 DOM 中被替换。
如果Buttonsrender返回不同类型的根元素,如下所示:
render() {
return createElement(this.state.clicks % 2 ? 'button' : 'a', { onClick: () => this.click() },
createElement(Timer, null),
'Clicks: ',
this.state.clicks
);
}
Run Code Online (Sandbox Code Playgroud)
然后在第一步中,a将与 the 进行比较button,因为它们是不同的类型,旧元素及其所有子元素将从 DOM 中删除、卸载并销毁。然后,将创建没有先前渲染结果的新元素,因此Timer将创建具有新状态的新实例,并且计时器将返回到 0。
Timer火柴? |
前一棵树 | 新树 |
|---|---|---|
| 不匹配 | <div><Timer /></div> |
<span><Timer /></span> |
| 匹配 | <div>a <Timer /> a</div> |
<div>b <Timer /> b</div> |
| 不匹配 | <div><Timer /></div> |
<div>first <Timer /></div> |
| 匹配 | <div>{false}<Timer /></div> |
<div>first <Timer /></div> |
| 匹配 | <div><Timer key="t" /></div> |
<div>first <Timer key="t" /></div> |
| 归档时间: |
|
| 查看次数: |
537 次 |
| 最近记录: |