假设我有一个具有条件渲染的视图组件:
render(){
if (this.state.employed) {
return (
<div>
<MyInput ref="job-title" name="job-title" />
</div>
);
} else {
return (
<div>
<MyInput ref="unemployment-reason" name="unemployment-reason" />
<MyInput ref="unemployment-duration" name="unemployment-duration" />
</div>
);
}
}
我的输入看起来像这样:
class MyInput extends React.Component {
...
render(){
return (
<div>
<input name={this.props.name}
ref="input"
type="text"
value={this.props.value || null}
onBlur={this.handleBlur.bind(this)}
onChange={this.handleTyping.bind(this)} />
</div>
);
}
}
让我们说 employed
是真的。每当我将其切换为 false 并且另一个视图呈现时,只有 unemployment-duration
被重新初始化。另外 unemployment-reason
预填充了 job-title
中的值(如果在条件更改之前给出了值)。
如果我将第二个渲染例程中的标记更改为如下所示:
render(){
if (this.state.employed) {
return (
<div>
<MyInput ref="job-title" name="job-title" />
</div>
);
} else {
return (
<div>
<span>Diff me!</span>
<MyInput ref="unemployment-reason" name="unemployment-reason" />
<MyInput ref="unemployment-duration" name="unemployment-duration" />
</div>
);
}
}
似乎一切正常。看起来 React 只是无法区分“职位”和“失业原因”。
请告诉我我做错了什么……
原文由 o01 发布,翻译遵循 CC BY-SA 4.0 许可协议
可能发生的情况是 React 认为在渲染之间只添加了一个
MyInput
(unemployment-duration
)。因此,job-title
永远不会被替换为unemployment-reason
,这也是交换预定义值的原因。当 React 进行 diff 时,它将根据组件的
key
属性确定哪些组件是新的,哪些是旧的。如果代码中没有提供这样的密钥,它将生成自己的密钥。您提供的最后一个代码片段之所以有效,是因为 React 本质上需要更改父元素下的所有元素的层次结构
div
我相信这会触发所有子元素的重新渲染(这就是为什么它作品)。如果您将span
添加到底部而不是顶部,则前面元素的层次结构不会改变,并且这些元素不会重新呈现(并且问题仍然存在)。这是官方 React 文档 所说的:
您应该能够通过向父
div
或所有MyInput
元素提供一个独特的key
元素来解决此问题。例如:
或者
现在,当 React 执行差异时,它会看到
divs
是不同的,并将重新渲染它,包括它的所有子项(第一个示例)。在第二个示例中,差异将在job-title
和unemployment-reason
上成功,因为它们现在有不同的密钥。您当然可以使用任何您想要的键,只要它们是唯一的。
2017 年 8 月更新
为了更好地了解键在 React 中的工作原理,我强烈建议阅读我对 理解 React.js 中唯一键的回答。
2017 年 11 月更新
此更新应该在不久前发布,但现在已弃用
ref
中的字符串文字。例如ref="job-title"
现在应该改为ref={(el) => this.jobTitleRef = el}
(例如)。有关更多信息,请参阅我对 使用 this.refs 的弃用警告 的回答。