如何强制重新安装反应组件?

假设我有一个视图组件,它有一个条件渲染:

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 只是无法区分“工作头衔”和“失业原因”。

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

140605 次浏览

在视图中使用 setState更改状态的 employed属性。

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

可能发生的是,反应认为只有一个 MyInput(unemployment-duration)之间的渲染被添加。因此,job-title永远不会被 unemployment-reason取代,这也是为什么要交换预定义的值。

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

您提供的上一个代码片段之所以能够工作,是因为 React 实际上需要更改父 div下所有元素的层次结构,我相信这会触发所有子元素的重新呈现(这就是它能够工作的原因)。如果您将 span添加到底部而不是顶部,那么前面元素的层次结构就不会改变,而且那些元素的层次结构也不会重新呈现(问题将持续存在)。

官方的 反应文件是这么说的:

当子元素被重新组合(比如在搜索结果中)或者新组件被添加到列表的前端(比如在流中)时,情况变得更加复杂。在这些情况下,每个子级的标识和状态必须通过呈现传递来维护,您可以通过为每个子级分配一个键来唯一地标识它。

当 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 的弃用警告的回答。

更改组件的键。

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

组件将被卸载,并且由于键已更改,将挂载一个 Component 的新实例。

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

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

我正在为我的应用程序 Crud 工作。这就是我如何做到它得到反作用带作为我的依赖。

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

里面有反应钩