如何强制重新安装 React 组件?
Posted
技术标签:
【中文标题】如何强制重新安装 React 组件?【英文标题】:How to force remounting on React components? 【发布时间】:2016-06-17 23:10:10 【问题描述】:假设我有一个具有条件渲染的视图组件:
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 只是无法区分 'job-title' 和 'unemployment-reason'。
请告诉我我做错了什么......
【问题讨论】:
employed
开关前后的每个 MyInput
(或 input
,如 DOM)元素上的 data-reactid
是什么?
@Chris 我未能指定 MyInput 组件呈现包装在 <div>
中的输入。 data-reactid
属性在包装 div 和输入字段上似乎都不同。 job-title
输入得到 id data-reactid=".0.1.1.0.1.0.1"
,而unemployment-reason
输入得到 id data-reactid=".0.1.1.0.1.2.1"
那么unemployment-duration
呢?
@Chris 抱歉,我说得太早了。在第一个示例中(没有“diff me”跨度)reactid
属性在 job-title
和 unemployment-reason
上是相同的,而在第二个示例中(使用 diff 跨度)它们是不同的。
@Chris for unemployment-duration
reactid
属性始终是唯一的。
【参考方案1】:
更改组件的键。
<Component key="1" />
<Component key="2" />
组件将被卸载并安装一个新的组件实例,因为密钥已更改。
编辑:记录在You Probably Don't Need Derived State:
当一个键改变时,React 会创建一个新的组件实例而不是更新当前的。键通常用于动态列表,但在这里也很有用。
【讨论】:
这在 React 文档中应该更加明显。这正是我所追求的,但花了几个小时才找到。 这对我帮助很大!谢谢!我需要重置 CSS 动画,但唯一的方法是强制重新渲染。我同意这应该在反应文档中更明显 @Alex.P.好的! CSS 动画有时会很棘手。我有兴趣看一个例子。 描述此技术的 React 文档:reactjs.org/blog/2018/06/07/… 谢谢。我对此非常感激。我尝试将随机数提供给未声明的道具,例如“randomNumber”,但唯一有效的道具是关键。【参考方案2】:可能发生的情况是 React 认为在渲染之间只添加了一个 MyInput
(unemployment-duration
)。因此,job-title
永远不会被 unemployment-reason
替换,这也是交换预定义值的原因。
当 React 进行 diff 时,它将根据 key
属性确定哪些组件是新的,哪些是旧的。如果代码中没有提供这样的密钥,它将生成自己的。
您提供的最后一个代码 sn-p 起作用的原因是,React 本质上需要更改父级 div
下所有元素的层次结构,我相信这会触发所有子级的重新渲染(这就是为什么有用)。如果您将 span
添加到底部而不是顶部,则前面元素的层次结构不会改变,并且这些元素不会重新渲染(并且问题仍然存在)。
官方React documentation是这样说的:
当孩子们被打乱(如在搜索结果中)或新组件被添加到列表的前面(如在流中)时,情况会变得更加复杂。在这些必须跨渲染通道维护每个子节点的身份和状态的情况下,您可以通过为其分配一个键来唯一标识每个子节点。
当 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 进行 diff 时,它会看到 divs
不同,并将重新渲染它,包括它的所有孩子(第一个示例)。在第二个示例中,差异将在 job-title
和 unemployment-reason
上成功,因为它们现在具有不同的键。
你当然可以使用任何你想要的键,只要它们是唯一的。
2017 年 8 月更新
为了更好地了解 React 中键的工作原理,我强烈建议阅读我对 Understanding unique keys in React.js 的回答。
2017 年 11 月更新
此更新应该在不久前发布,但现在已弃用在 ref
中使用字符串文字。例如,ref="job-title"
现在应该改为 ref=(el) => this.jobTitleRef = el
(例如)。请参阅我对Deprecation warning using this.refs 的回复了解更多信息。
【讨论】:
it will determine which components are new and which are old based on their key property.
- 这是...关键...我的理解
非常感谢!我还发现地图中的所有子组件都已成功重新渲染,但我不明白为什么。
一个很好的提示是使用状态变量(它会发生变化,例如 id)作为 div 的键!【参考方案3】:
在您的视图中使用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>
);
【讨论】:
我已经这样做了。 'employed' 变量是视图组件状态的一部分,它通过它的 setState 函数进行更改。很抱歉没有解释。 'employed' 变量应该是 React 状态的属性。在您的代码示例中并不明显。 如何更改 this.state.employees?请显示代码。您确定在代码中的适当位置使用 setState 吗? 视图组件比我的示例显示的要复杂一些。除了 MyInput 组件之外,它还呈现一个单选按钮组,该组使用 onChange 处理程序控制 'employed' 的值。在 onChange 处理程序中,我相应地设置了视图组件的状态。当单选按钮组的值发生变化时,视图重新渲染得很好,但 React 认为第一个 MyInput-component 与 'employed' 更改之前相同。【参考方案4】:我正在为我的应用程序开发 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!您可能想查看how to write a good answer。除了发布代码之外,对您如何解决问题添加一些说明。以上是关于如何强制重新安装 React 组件?的主要内容,如果未能解决你的问题,请参考以下文章
什么可能导致 React Native 组件不断地卸载和重新安装?