如何强制重新安装 React 组件?

IT技术 javascript reactjs
2021-02-23 13:12:58

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

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(如果在条件更改之前给出了值)。

如果我将第二个渲染例程中的标记更改为如下所示:

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 无法区分“职位”和“失业原因”。

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

4个回答

更改组件的键。

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

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

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

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

谢谢你。我对此非常感激。我尝试将随机数提供给诸如“randomNumber”之类的未声明道具,但唯一有效的道具是关键。
2021-04-29 13:12:58
@Alex.P. 好的!CSS 动画有时会很棘手。我很想看一个例子。
2021-05-07 13:12:58
这在 React 文档中应该更明显。这正是我所追求的,但花了几个小时才找到。
2021-05-10 13:12:58
描述此技术的 React 文档:reactjs.org/blog/2018/06/07/...
2021-05-11 13:12:58
嗯,这对我帮助很大!谢谢!我需要重置 CSS 动画,但唯一的方法是强制重新渲染。我同意这在反应文档中应该更明显
2021-05-13 13:12:58

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

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

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

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 时,它会发现divs它们不同,并将重新渲染它,包括它的所有子元素(第一个示例)。在第二个示例中,差异将成功job-titleunemployment-reason因为它们现在具有不同的键。

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


2017 年 8 月更新

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


2017 年 11 月更新

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

一个很好的提示是使用一个状态变量(它会改变,例如一个 id。)作为 div 的键!
2021-04-27 13:12:58
非常感谢 !我还发现地图中的所有子组件都已成功重新渲染,但我不明白为什么。
2021-05-01 13:12:58
it will determine which components are new and which are old based on their key property. - 那是……关键……我的理解
2021-05-09 13:12:58

setState在您的视图中使用以更改employed状态属性。这是 React 渲染引擎的示例。

 someFunctionWhichChangeParamEmployed(isEmployed) {
      this.setState({
          employed: isEmployed
      });
 }

 getInitialState() {
      return {
          employed: true
      }
 },

 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 状态的属性。在您的代码示例中并不明显。
2021-04-21 13:12:58
我已经在做这个了。“已使用”变量是视图组件状态的一部分,它通过其 setState 函数进行更改。抱歉没有解释。
2021-05-02 13:12:58
你如何改变这个状态。就业?请出示代码。您确定在代码中的适当位置使用 setState 吗?
2021-05-09 13:12:58
视图组件比我的示例显示的要复杂一些。除了 MyInput 组件之外,它还呈现一个单选按钮组,该组通过 onChange 处理程序控制“已雇用”的值。在 onChange 处理程序中,我相应地设置了视图组件的状态。当单选按钮组的值发生变化时,视图重新呈现良好,但 React 认为第一个 MyInput-component 与“已使用”更改之前相同。
2021-05-17 13:12:58

我正在为我的应用程序开发 Crud。这就是我做的方式 将 Reactstrap 作为我的依赖项。

import React, { useState, setState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import firebase from 'firebase';
// import { LifeCrud } from '../CRUD/Crud';
import { Row, Card, Col, Button } from 'reactstrap';
import InsuranceActionInput from '../CRUD/InsuranceActionInput';

const LifeActionCreate = () => {
  let [newLifeActionLabel, setNewLifeActionLabel] = React.useState();

  const onCreate = e => {
    const db = firebase.firestore();

    db.collection('actions').add({
      label: newLifeActionLabel
    });
    alert('New Life Insurance Added');
    setNewLifeActionLabel('');
  };

  return (
    <Card style={{ padding: '15px' }}>
      <form onSubmit={onCreate}>
        <label>Name</label>
        <input
          value={newLifeActionLabel}
          onChange={e => {
            setNewLifeActionLabel(e.target.value);
          }}
          placeholder={'Name'}
        />

        <Button onClick={onCreate}>Create</Button>
      </form>
    </Card>
  );
};

那里有一些 React Hooks

欢迎来到 SO!您可能想复习一下如何写出一个好的答案添加一些关于您如何解决问题的解释,而不仅仅是发布代码。
2021-05-19 13:12:58