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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35792275/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:13:28  来源:igfitidea点击:

How to force remounting on React components?

javascriptreactjs

提问by o01

Lets say I have a view component that has a conditional render:

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

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 looks something like this:

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

Lets say employedis true. Whenever I switch it to false and the other view renders, only unemployment-durationis re-initialized. Also unemployment-reasongets prefilled with the value from job-title(if a value was given before the condition changed).

可以说employed是真的。每当我将其切换为 false 并且其他视图呈现时,只会unemployment-duration重新初始化。还unemployment-reason预填充了来自的值job-title(如果在条件更改之前给出了值)。

If I change the markup in the second rendering routine to something like this:

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

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

It seems like everything works fine. Looks like React just fails to diff 'job-title' and 'unemployment-reason'.

似乎一切正常。看起来 React 无法区分“职位”和“失业原因”。

Please tell me what I'm doing wrong...

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

回答by Alex K

Change the key of the component.

更改组件的键。

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

Component will be unmounted and a new instance of Component will be mounted since the key has changed.

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

edit: Documented on You Probably Don't Need Derived State:

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

When a key changes, React will create a new component instance rather than update the current one. Keys are usually used for dynamic lists but are also useful here.

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

回答by Chris

What's probably happening is that React thinks that only one MyInput(unemployment-duration) is added between the renders. As such, the job-titlenever gets replaced with the unemployment-reason, which is also why the predefined values are swapped.

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

When React does the diff, it will determine which components are new and which are old based on their keyproperty. If no such key is provided in the code, it will generate its own.

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

The reason why the last code snippet you provide works is because React essentially needs to change the hierarchy of all elements under the parent divand I believe that would trigger a re-render of all children (which is why it works). Had you added the spanto the bottom instead of the top, the hierarchy of the preceding elements wouldn't change, and those element's wouldn't re-render (and the problem would persist).

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

Here's what the official React documentationsays:

React官方文档是这样说的:

The situation gets more complicated when the children are shuffled around (as in search results) or if new components are added onto the front of the list (as in streams). In these cases where the identity and state of each child must be maintained across render passes, you can uniquely identify each child by assigning it a key.

When React reconciles the keyed children, it will ensure that any child with key will be reordered (instead of clobbered) or destroyed (instead of reused).

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

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

You should be able to fix this by providing a unique keyelement yourself to either the parent divor to all MyInputelements.

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

For example:

例如:

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

OR

或者

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

Now, when React does the diff, it will see that the divsare different and will re-render it including all of its' children (1st example). In the 2nd example, the diff will be a success on job-titleand unemployment-reasonsince they now have different keys.

现在,当 React 进行 diff 时,它会看到divs它们不同,并将重新渲染它,包括它的所有子元素(第一个示例)。在第二个示例中,差异将成功job-titleunemployment-reason因为它们现在具有不同的键。

You can of course use any keys you want, as long as they are unique.

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



Update August 2017

2017 年 8 月更新

For a better insight into how keys work in React, I strongly recommend reading my answer to Understanding unique keys in React.js.

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



Update November 2017

2017 年 11 月更新

This update should've been posted a while ago, but using string literals in refis now deprecated. For example ref="job-title"should now instead be ref={(el) => this.jobTitleRef = el}(for example). See my answer to Deprecation warning using this.refsfor more info.

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

回答by Dmitriy

Use setStatein your view to change employedproperty of state. This is example of React render engine.

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

回答by Ruben Verster

I'm working on Crud for my app. This is how I did it Got Reactstrap as my dependency.

我正在为我的应用程序开发 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>
  );
};

Some React Hooks in there

那里有一些 React Hooks