Facebook says我不应该将React组件保留在其父级状态中。相反,我应该在每次运行时在render方法中重新创建子项。
什么不应该进入州?
反应组件:基于底层道具和渲染()构建它们 状态。
现在我的问题是:我该怎么做?它甚至可能吗?如果我从头开始重新创建子组件,那么状态是否会丢失?
我能想到的这种情况唯一可行的方法是,只有一个状态对象,它属于根组件。其余的组件只有props
,每当他们想要更新他们的某些状态时,他们需要调用一些父级的处理程序一直到根组件,因为它是唯一具有状态对象的组件!一旦更新,root将为子组件返回其状态props
。我认为这根本不实用!
[UPDATE]
这是一个示例代码,我发现很难不将组件存储在父级状态中:
http://codepen.io/mehranziadloo/pen/XdLvgq
class BlackBox extends React.Component
{
constructor() {
super();
this.state = {
counter: 0
};
}
increment() {
this.setState({ counter: this.state.counter+1 });
}
render() {
return (
<span onClick={this.increment.bind(this)} style={{
fontSize: '24pt',
border: '1px solid black',
margin: 10,
padding: 10,
}}>
{this.state.counter}
</span>
);
}
}
class RedBox extends React.Component
{
constructor() {
super();
this.state = {
counter: 0
};
}
increment() {
this.setState({ counter: this.state.counter+1 });
}
render() {
return (
<span onClick={this.increment.bind(this)} style={{
fontSize: '24pt',
border: '1px solid red',
margin: 10,
padding: 10,
}}>
{this.state.counter}
</span>
);
}
}
class Parent extends React.Component
{
constructor() {
super();
this.state = {
childCmps: [],
};
}
addBlackBox() {
let newState = this.state.childCmps.slice();
newState.push(<BlackBox key={newState.length} />);
this.setState({
childCmps: newState
});
}
addRedBox() {
let newState = this.state.childCmps.slice();
newState.push(<RedBox key={newState.length} />);
this.setState({
childCmps: newState
});
}
render() {
let me = this;
return (
<div>
<button onClick={this.addBlackBox.bind(this)}>Add Black Box</button>
<button onClick={this.addRedBox.bind(this)}>Add Red Box</button>
<br /><br />
{this.state.childCmps}
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
答案 0 :(得分:3)
如果从头开始重新创建子组件,那么状态是否会丢失?
不,因为React在内部管理支持实例(保持状态),并且如果两次调用render()
说要呈现该组件,则不会替换它们。
换句话说:
ReactDOM.render(<MyComponent />, div);
ReactDOM.render(<MyComponent />, div);
这将不创建MyComponent
两次,但只有一次。它将渲染两次:它第一次不存在,所以它创建它,第二次它已经存在,所以它会更新它。可以保留在两个渲染过程之间设置的任何内部状态。
React经过优化,允许您简单地创建完整的声明性渲染函数,并确定实现渲染所需的更改。
<强>更新强>
您发布的示例是在动态子级列表中使用键。键是识别特定子项(以及它们存在的位置)的一种方法,因此您需要小心不来更改维护状态元素的渲染过程之间的键。
而不是将实际呈现的组件存储在状态中,例如<BlackBox key={i} />
,而是存储必要的数据以呈现组件,例如组件类BlackBox
和{{1}的唯一标识符}。 (仅供参考,你不应该使用索引作为密钥,因为索引可以改变。我建议使用一个总是递增的计数器。)
这里修改的key
类没有将呈现的组件存储在状态中(其他组件可以保持原样):
Parent
注意:
class Parent extends React.Component {
static blackCount = 0;
static redCount = 0;
state = {
childCmps: [],
};
constructor(props, context) {
super(props, context);
}
addBlackBox = () => {
this.setState({
childCmps: [...this.state.childCmps, { Component: BlackBox, id: "black" + (++Parent.blackCount) }]
});
};
addRedBox = () => {
this.setState({
childCmps: [...this.state.childCmps, { Component: RedBox, id: "red" + (++Parent.redCount) }]
});
};
render() {
return (
<div>
<button onClick={this.addBlackBox}>Add Black Box</button>
<button onClick={this.addRedBox}>Add Red Box</button>
<br /><br />
{this.state.childCmps.map(child => <child.Component key={child.id} />)}
</div>
);
}
}
(又名全局)道具来计算添加了多少个黑色和红色框,并结合字符串&#34; red&#34;和&#34;黑&#34;形成独特的键。 (如果您没有support for class properties,可以使用static
等来初始化静态类属性。)Parent.blackCount = 0
在正确的范围内。 (如果您不支持类属性,则可以在构造函数中使用this
。)this.addBlackBox = this.addBlackBox.bind(this)
初始化移动到了一个类属性。您可以猜测,我强烈建议您使用类属性。 :)state
函数中,每个框组件始终使用Parent/render()
map()
的{{1}}状态重新呈现。答案 1 :(得分:1)
您只需要在父状态下保留渲染子组件所需的任何数据。通常,这只是您想要传递的道具或组件的类型
在您的情况下,这只是组件的颜色&#34;红色&#34;或&#34;黑&#34;。
所以在父状态中,包含值为&#34; Red&#34;的字符串的数组。或&#34;黑&#34;足够。
每次单击其中一个按钮时,只需将另一个项添加到数组中,然后再次设置状态。这样的事情。
addRedBox() {
let newChildList = this.state.childList.slice();
newChildList.push("Red");
this.setState({
childList: newChildList
});
}
然后在render()
函数中执行此操作:
{this.state.childList.map(function(color,i) {
if (color=="Black") {
return <BlackBox key={i} />
} else {
return <RedBox key={i} />
}
})}
在重新渲染时,您只需将新道具(如果有)传递给子组件,然后每个子组件也将使用新道具重新渲染。
将新道具传递给孩子将不重置子组件。它将再次运行所有生命周期方法(包括render()
)。
答案 2 :(得分:0)
组件仅在状态更改(更新)时呈现,并且您应该保持状态简单,使用NSObject+Extensions.h
- (NSString *)className;
NSObject+Extensions.m
- (NSString *)className {
return NSStringFromClass(self.class);
}
与子组件通信。
当您的应用程序变大时,您可以使用Flux或Redux来管理您的状态
答案 3 :(得分:0)
您正试图在React中看到面向对象的方法。别。有OO,然后就是Facebook所做的一切。
不,根据您引用的文档,您无法将组件存储在状态中。你可以尝试一下,但你会发现一些东西都不起作用。
以下是OO类(伪代码)的示例:
class Parent {
list children
temporarilyAbondonChildren() {
for each child in children {
Orphanage.leaveAtDoorStep( child )
}
doStuffForTenYears()
for each child in Children {
output child.isOk()
}
}
}
这里是React中最接近它的东西:
class Parent {
temporarilyAbandonChildren() {
doStuffForTenYears()
child_properties = Orphanage.whatWouldveHappenedToMyKidHadIGivenBirthAndLeftThemForTenYears()
children = renderImaginaryChildren( child_properties )
for each child in children {
output child.isOk()
}
}
}
我能想到的这种情况唯一可行的方法是,只有一个状态对象,它属于根组件。其余的组件只有道具,每当他们想要更新他们的某些状态时,他们需要调用一些父级的处理程序一直到根组件,因为它是唯一具有状态对象的组件!一旦更新,root将为子组件返回其状态作为道具。我认为这根本不实用!
我同意。