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

2022-08-30 02:41:26

假设我有一个具有条件呈现的视图组件:

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>
        );
    }
}

让我们说这是真的。每当我将其切换为 false 并且其他视图呈现时,只会重新初始化。还会使用 from 中的值进行预填充(如果在条件更改之前给出了值)。employedunemployment-durationunemployment-reasonjob-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 只是无法区分“职位名称”和“失业原因”。

请告诉我我做错了什么...


答案 1

更改组件的键。

<Component key="1" />
<Component key="2" />

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

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

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


答案 2

可能发生的事情是,React 认为在渲染之间只添加了一个 ()。因此,never 会被替换为 ,这也是交换预定义值的原因。MyInputunemployment-durationjob-titleunemployment-reason

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

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

以下是官方 React 文档的内容:

当子级被洗牌(如在搜索结果中)或将新组件添加到列表的前面(如在流中)时,情况变得更加复杂。在这些情况下,必须跨渲染传递维护每个子级的标识和状态,您可以通过为每个子级分配一个键来唯一标识每个子级。

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

您应该能够通过自己向父元素或所有元素提供唯一元素来解决此问题。keydivMyInput

例如:

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 时,它会看到 它们不同,并将重新呈现它,包括它的所有子级(第 1 个示例)。在第二个示例中,diff 将成功,因为它们现在具有不同的键。divsjob-titleunemployment-reason

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


2017 年 8 月更新

为了更好地了解 Keys 在 React 中是如何工作的,我强烈建议阅读我对理解 React 中唯一密钥的回答.js。


2017 年 11 月更新

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