ReactJS 状态与道具

这可能在可回答性和固执己见之间徘徊,但是我将反复讨论如何随着复杂性的增长构建 ReactJS 组件,并且可以使用一些方向。

来自 AngularJS,我想将我的模型作为一个属性传递给组件,并让组件直接修改模型。或者我应该将模型分解成各种 state属性,并在发送回上游时将其重新编译在一起?ReactJS 的方法是什么?

Take the example of a blog post editor. Trying to modify the model directly ends up looking like:

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

这看起来不对。

是不是更像是使我们的 text模型属性为 state,并在保存之前将其编译回模型中的 React 方式,比如:

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的第一种方法是否可行?

50760 次浏览

你的第二种方法更像是。React 关心的不是模型,而是 价值观以及它们是如何流经你的应用程序的。理想情况下,您的文章模型将存储在根目录下的单个组件中。然后创建每个子组件使用模型的各个部分。

您可以将回调传递给需要修改数据的子组件,并从子组件调用它们。

直接修改 this. props 或 this. state 不是一个好主意,因为 React 将无法识别更改。这是因为 React 对你的文章道具做了一个浅显的比较,以确定它是否发生了变化。

我制作这个 Jsfiddle是为了展示数据如何从外部组件流向内部组件。

handleClick方法显示了正确更新状态的3种方法:

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

来自反应部门的文件

Props 是不可变的: 它们从父节点传递,并由父节点“拥有”。为了实现交互,我们向组件引入可变状态。State 对于组件是私有的,可以通过调用 this.setState ()进行更改。更新状态时,组件将重新呈现自己。

尝试空间: 当道具(或状态)更新(通过 setProps/setState 或父)组件也重新呈现。

2016年更新: 反应变了,解释“道具 VS 状态”变得非常简单。如果一个组件需要更改数据-将其置于某种状态,否则置于道具中。因为现在 道具是只读的了。

道具和州政府到底有什么区别?

你可以找到很好的解释 给你(完整版)

Changing props and state

来自 反应中思考的读数:

让我们通过每一个,并找出哪一个是状态。简单 就每一项数据问三个问题:

  1. 是父母通过道具传递的吗? 如果是的话,可能不是 国家。
  2. 它是否会随着时间而改变? 如果不是,它可能不是状态。

  3. 你能计算它的基础上,任何其他状态或道具在您的 组件? 如果是,它就不是状态。

我不确定我是否回答了你的问题,但是我发现,特别是在一个大型/不断增长的应用程序中,Container/Component 模式工作得非常好。

基本上你有两个反应组件:

  • 一个“纯”显示组件,处理样式和 DOM 交互;
  • 一个容器组件,它处理访问/保存外部数据、管理状态和呈现显示组件。

Example

这个例子可能太简单了,无法说明这个模式的好处,因为对于这样一个简单的例子来说,它非常冗长。

/**
* 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 很大程度上启发了我的回答(我最近用了很多) 复制(和 还原反应) ,它实现了这种模式非常好。

2018年或以后阅读本文的读者请注意:

自从这个答案被写出来以后,反应已经发生了很大的变化,特别是随着 钩子的引入。但是,这个示例中的底层状态管理逻辑保持不变,更重要的是,保持状态逻辑和表示逻辑分离所带来的好处仍然以相同的方式适用。

I think you're using an anti-pattern which Facebook has already explained at this 链接

你会发现:

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: ‘ bar’}作为值道具。如果用户单击锚点,父组件的状态将被更新为{ value: { foo: ‘ barbar’}} ,触发内部组件的重新呈现过程,该过程将接收{ foo: ‘ barbar’}作为道具的新值。

问题是,由于父组件和内部组件共享对同一个对象的引用,当对象在 onClick 函数的第2行发生变化时,内部组件的支撑将发生变化。因此,当重新呈现过程启动,并且应该调用 this.props.value.foo 时,它将等于 nextProps.value.foo,因为实际上,this.props.value 引用与 nextProps.value 相同的对象。

因此,由于我们将错过对道具的更改和短路的重新渲染过程,用户界面将不会得到更新从’酒吧’到’酒吧’