可以说我有一个具有条件渲染的视图组件:
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> ); } }
MyInput看起来像这样:
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(如果条件更改之前已给出值)。
employed
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只是无法区分’职位-头衔’和’失业原因’。
请告诉我我做错了什么…
可能发生的事情是React认为在渲染之间仅添加了一个MyInput(unemployment-duration)。因此,job- title永不替换为unemployment-reason,这也是为什么交换预定义值的原因。
MyInput
unemployment-duration
job- title
当React做比较时,它将根据它们的key属性确定哪些是新组件,哪些是旧组件。如果代码中未提供此类密钥,它将生成自己的密钥。
key
您提供的最后一个代码段起作用的原因是,React本质上需要更改父级下所有元素的层次结构,div我相信这会触发所有子级的重新渲染(这就是它起作用的原因)。如果您将span而不是顶部添加到底部,则前面的元素的层次结构不会改变,并且这些元素的层次也不会重新呈现(问题将继续存在)。
div
span
这是官方的React文档说的:
当孩子们随机移动(如在搜索结果中)或将新组件添加到列表的开头(如在流中)时,情况变得更加复杂。在这些情况下,必须在整个渲染过程中维护每个孩子的身份和状态,您可以通过为每个孩子分配一个密钥来唯一地标识每个孩子。 当React调和带键的子代时,它将确保所有带键的子代都将重新排序(而不是破坏)或销毁(而不是重新使用)。
当孩子们随机移动(如在搜索结果中)或将新组件添加到列表的开头(如在流中)时,情况变得更加复杂。在这些情况下,必须在整个渲染过程中维护每个孩子的身份和状态,您可以通过为每个孩子分配一个密钥来唯一地标识每个孩子。
当React调和带键的子代时,它将确保所有带键的子代都将重新排序(而不是破坏)或销毁(而不是重新使用)。
您应该能够通过向key父级div或所有MyInput元素自己提供一个唯一元素来解决此问题。
例如:
render(){ if (this.state.employed) { return ( <div key="employed"> <MyInput ref="job-title" name="job-title" /> </div> ); } else { return ( <div key="notEmployed"> <MyInput ref="unemployment-reason" name="unemployment-reason" /> <MyInput ref="unemployment-duration" name="unemployment-duration" /> </div> ); } }
要么
render(){ if (this.state.employed) { return ( <div> <MyInput key="title" ref="job-title" name="job-title" /> </div> ); } else { return ( <div> <MyInput key="reason" ref="unemployment-reason" name="unemployment-reason" /> <MyInput key="duration" ref="unemployment-duration" name="unemployment-duration" /> </div> ); } }
现在,当React执行diff时,它会发现diff divs是不同的,并将重新渲染它,包括它的所有子对象(第一个示例)。在第二个示例中,差异比较成功job-title,unemployment- reason因为它们现在具有不同的键。
divs
unemployment- reason
当然,您可以使用任何想要的键,只要它们是唯一的即可。
2017年8月更新
为了更好地了解键在React中的工作方式,我强烈建议阅读我对理解React.js中的唯一键的回答。
2017年11月更新
此更新应该已经发布了一段时间,但是ref现在不建议使用in中的字符串。例如ref="job-title",现在应该改为ref={(el) =>this.jobTitleRef =el}(例如)。
ref
ref="job-title"
ref={(el) =>this.jobTitleRef =el}