这可能是在回答问题和自以为是之间的界限,但我会反复思考如何构建ReactJS组件,因为复杂性增加并且可以使用某些方向。
来自AngularJS,我希望将我的模型作为属性传递给组件,并让组件直接修改模型。或者我应该将模型拆分为各种state
属性,并在向上游发送时将其重新编译回来?什么是ReactJS方式?
以博客文章编辑器为例。试图直接修改模型最终看起来像:
var PostEditor = React.createClass({
updateText: function(e) {
var text = e.target.value;
this.props.post.text = text;
this.forceUpdate();
},
render: function() {
return (
<input value={this.props.post.text} onChange={this.updateText}/>
<button onClick={this.props.post.save}/>Save</button>
);
}
});
这似乎不对。
更多是React方式来制作我们的text
模型属性state
,并在保存之前将其编译回模型:
var PostEditor = React.createClass({
getInitialState: function() {
return {
text: ""
};
},
componentWillMount: function() {
this.setState({
text: this.props.post.text
});
},
updateText: function(e) {
this.setState({
text: e.target.value
});
},
savePost: function() {
this.props.post.text = this.state.text;
this.props.post.save();
},
render: function() {
return (
<input value={this.state.text} onChange={this.updateText}/>
<button onClick={this.savePost}/>Save</button>
);
}
});
这并不需要调用this.forceUpdate()
,但随着模型的增长,(帖子可能包含作者,主题,标签,评论,评级等等),该组件开始获取真的很复杂。
第一种方法是ReactLink的方法吗?
答案 0 :(得分:95)
正在更新2016: React被改变了,解释&#34; props vs state&#34;变得非常简单。如果组件需要更改数据 - 将其置于某个状态,否则将置于道具中。因为道具现在是只读的。
道具和州之间的确切区别是什么?
你可以找到很好的解释here(完整版)
答案 1 :(得分:63)
你的第二种方法更像是它。 React并不关心模型,因为它关心值以及它们如何在您的应用程序中流动。理想情况下,您的帖子模型将存储在根目录中的单个组件中。然后创建子组件,每个组件都使用模型的一部分。
您可以将回调传递给需要修改数据的子项,并从子组件中调用它们。
直接修改this.props或this.state不是一个好主意,因为React将无法接收更改。这是因为React对你的后道具进行浅层比较,以确定它是否已经改变。
我制作了这个jsfiddle来展示数据如何从外部组件流向内部组件:
http://jsfiddle.net/jxg/M3CLB/
handleClick
方法显示了3种方式(im)正确更新状态:
var Outer = React.createClass({
getInitialState: function() {
return {data: {value: 'at first, it works'}};
},
handleClick: function () {
// 1. This doesn't work, render is not triggered.
// Never set state directly because the updated values
// can still be read, which can lead to unexpected behavior.
this.state.data.value = 'but React will never know!';
// 2. This works, because we use setState
var newData = {value: 'it works 2'};
this.setState({data: newData});
// 3. Alternatively you can use React's immutability helpers
// to update more complex models.
// Read more: http://facebook.github.io/react/docs/update.html
var newState = React.addons.update(this.state, {
data: {value: {$set: 'it works'}}
});
this.setState(newState);
},
render: function() {
return <Inner data={this.state.data} handleClick={this.handleClick} />;
}
});
答案 2 :(得分:35)
来自React doc
道具是不可变的:它们是从父母那里传来的,并且是“拥有的”#34;由父母。为了实现交互,我们向组件引入了可变状态。 this.state是组件的私有,可以通过调用this.setState()来更改。更新状态后,组件将重新呈现自己。
从 TrySpace :当更新道具(或状态)时(通过setProps / setState或parent),组件也会重新渲染。
答案 3 :(得分:16)
来自Thinking in React的阅读:
让我们通过每一个,找出哪一个是州。只是 询问关于每个数据的三个问题:
- 是通过道具从父母那里传来的吗?如果是这样,可能不是 州。
它会随着时间的推移而改变吗?如果没有,它可能不是州。
- 醇>
你能根据你的任何其他状态或道具来计算吗? 零件?如果是这样,那就不是状态了。
答案 4 :(得分:11)
我不确定我是否在回答您的问题,但我发现,特别是在大型/不断发展的应用程序中,容器/组件模式运行得非常好。
基本上你有两个React组件:
N.B。这个例子可能过于简单,无法说明这种模式的好处,因为对于这种直截了当的案例来说,它非常冗长。
/**
* Container Component
*
* - Manages component state
* - Does plumbing of data fetching/saving
*/
var PostEditorContainer = React.createClass({
getInitialState: function() {
return {
text: ""
};
},
componentWillMount: function() {
this.setState({
text: getPostText()
});
},
updateText: function(text) {
this.setState({
text: text
});
},
savePost: function() {
savePostText(this.state.text);
},
render: function() {
return (
<PostEditor
text={this.state.text}
onChange={this.updateText.bind(this)}
onSave={this.savePost.bind(this)}
/>
);
}
});
/**
* Pure Display Component
*
* - Calculates styling based on passed properties
* - Often just a render method
* - Uses methods passed in from container to announce changes
*/
var PostEditor = React.createClass({
render: function() {
return (
<div>
<input type="text" value={this.props.text} onChange={this.props.onChange} />
<button type="button" onClick={this.props.onSave} />
</div>
);
}
});
通过将显示逻辑和数据/状态管理分开,您有一个可重复使用的显示组件:
您还有一个处理所有外部通信的容器组件。如果您稍后在*上进行任何重大更改,这将使您更容易灵活地访问数据。
这种模式也使编写和实现单元测试变得更加直接。
迭代了一个大的React应用程序几次后,我发现这种模式让事情变得相对轻松,特别是当你有更大的组件具有计算样式或复杂的DOM交互时。
*阅读助焊剂模式,并查看 Marty.js,这很大程度上激发了这个答案(我最近一直在使用很多) Redux(和react-redux),非常好地实现了这种模式。
请注意2018年或之后阅读此内容的人:
自从写完这个答案以来,React已经发展了很多,特别是在引入Hooks之后。但是,此示例中的基础状态管理逻辑保持不变,更重要的是,保持状态和表示逻辑分离所带来的好处仍然以相同的方式应用。
答案 5 :(得分:0)
我认为您正在使用Facebook已经在此处解释的反模式link
这是你发现的事情:
React.createClass({
getInitialState: function() {
return { value: { foo: 'bar' } };
},
onClick: function() {
var value = this.state.value;
value.foo += 'bar'; // ANTI-PATTERN!
this.setState({ value: value });
},
render: function() {
return (
<div>
<InnerComponent value={this.state.value} />
<a onClick={this.onClick}>Click me</a>
</div>
);
}
});
第一次渲染内部组件时,它会有{foo:&#39; bar&#39;作为价值道具。如果用户点击锚点,则父组件的状态将更新为{value:{foo:&#39; barbar&#39;触发内部组件的重新呈现过程,它将接收{foo:&#39; barbar&#39; }作为道具的新价值。
问题是,由于父组件和内组件共享对同一对象的引用,当对象在onClick函数的第2行上变异时,内部组件所具有的prop将会改变。因此,当重新呈现过程开始并调用shouldComponentUpdate时,this.props.value.foo将等于nextProps.value.foo,因为实际上this.props.value引用与nextProps.value相同的对象。
因此,由于我们错过了道具上的更改并使重新渲染过程短路,因此UI无法从“条形码”中更新。到&#39; barbar&#39;