如何强制重新挂载 React 组件?

新手上路,请多包涵

假设我有一个具有条件渲染的视图组件:

 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 许可协议

阅读 1k
2 个回答

可能发生的情况是 React 认为在渲染之间只添加了一个 MyInput ( unemployment-duration )。因此, job-title 永远不会被替换为 unemployment-reason ,这也是交换预定义值的原因。

当 React 进行 diff 时,它将根据组件的 key 属性确定哪些组件是新的,哪些是旧的。如果代码中没有提供这样的密钥,它将生成自己的密钥。

您提供的最后一个代码片段之所以有效,是因为 React 本质上需要更改父元素下的所有元素的层次结构 div 我相信这会触发所有子元素的重新渲染(这就是为什么它作品)。如果您将 span 添加到底部而不是顶部,则前面元素的层次结构不会改变,并且这些元素不会重新呈现(并且问题仍然存在)。

这是官方 React 文档 所说的:

当孩子被打乱(如在搜索结果中)或如果新组件被添加到列表的前面(如在流中)时,情况会变得更加复杂。在这些情况下,每个孩子的身份和状态必须在渲染过程中保持不变,您可以通过为其分配一个键来唯一标识每个孩子。

当 React 协调键控子级时,它将确保任何具有键的子级将被重新排序(而不是破坏)或销毁(而不是重用)。

您应该能够通过向父 div 或所有 MyInput 元素提供一个独特的 key 元素来解决此问题。

例如:

 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 执行差异时,它会看到 divs 是不同的,并将重新渲染它,包括它的所有子项(第一个示例)。在第二个示例中,差异将在 job-titleunemployment-reason 上成功,因为它们现在有不同的密钥。

您当然可以使用任何您想要的键,只要它们是唯一的。


2017 年 8 月更新

为了更好地了解键在 React 中的工作原理,我强烈建议阅读我对 理解 React.js 中唯一键的回答


2017 年 11 月更新

此更新应该在不久前发布,但现在已弃用 ref 中的字符串文字。例如 ref="job-title" 现在应该改为 ref={(el) => this.jobTitleRef = el} (例如)。有关更多信息,请参阅我对 使用 this.refs 的弃用警告 的回答。

原文由 Chris 发布,翻译遵循 CC BY-SA 4.0 许可协议

更改组件的键。

 <Component key="1" />

 <Component key="2" />

由于密钥已更改,组件将被卸载并安装组件的新实例。

编辑:记录在 你可能不需要派生状态

当键更改时,React 将创建一个新的组件实例而不是更新当前组件实例。键通常用于动态列表,但在这里也很有用。

原文由 Alex K 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题