我正在尝试为数据结构创建一个简单的基于网格的编辑器,而且我对React.js有一些概念上的问题。他们的文档对此没有多大帮助,所以我希望有人可以提供帮助。
首先,将状态从外部组件转移到内部组件的正确方法是什么?是否有可能将内部组件中的状态更改“冒泡”到外部组件?
第二,两个独立的组件可以共享数据,从而一个中的突变在另一个中可见吗?
以下是我想要做的事情的简化示例(JSFiddle version):
我有一个包含company
个对象数组的employee
对象。我想在可编辑网格中列出employee
列表。当我单击按钮时,我希望看到生成的company
对象以及任何突变(写入控制台)。
/** @jsx React.DOM */
var Cell = React.createClass({
getInitialState: function () {
return {data: ""};
},
componentWillMount: function () {
this.setState({data: this.props.data});
},
onChange: function (evt) {
console.log(this.state, evt.target.value);
this.setState({data: evt.target.value});
},
render: function () {
var data = this.props.data;
return <input value={this.state.data} onChange={this.onChange} />
}
});
var Row = React.createClass({
render: function () {
return (<div className="row">
<Cell data={this.props.data.name} />
<Cell data={this.props.data.location} />
<Cell data={this.props.data.phone} />
</div>);
}
});
var Grid = React.createClass({
render: function () {
var rows = this.props.data.map(function (rowData, index) {
return <Row key={index} data={rowData}>Row</Row>;
});
return <div className="table">{rows}</div>;
}
});
var Button = React.createClass({
getInitialState: function () {
return {data: {}}
},
componentWillMount: function () {
this.setState({data: this.props.data});
},
onClick: function () {
console.log(this.state);
},
render: function () {
return <button onClick={this.onClick}>Update</button>;
}
});
var company = {
name: "Innotech",
employees: [
{id: "1", name: "Peter", location: "IT", phone: "555-1212"},
{id: "2", name: "Samir", location: "IT", phone: "555-1213"},
{id: "3", name: "Milton", location: "loading dock", phone: "none"}
]
};
React.renderComponent(
<div><Grid data={company.employees} /><Button data={company} /></div>,
document.getElementById('employees')
);
答案 0 :(得分:53)
我认为这是React目前最缺乏证据的部分。在组件之间进行通信的建议方法是在从父节点传送到子节点时简单地设置props,并在从子节点到父节点进行通信时通过props传递回调。
如果您希望在兄弟组件之间共享数据,则意味着应该有一个父组件来管理状态并将其传递给两个组件。大多数情况下,您的州应居住在组件层次结构的顶部附近,并且每条信息都应该(至多)存在于一个组件的状态中,而不是更多。
有关此内容的更多信息,请参阅Pete Hunt的博文 Thinking in React 。
考虑到这一点,我已经更新了你的小提琴:
http://jsfiddle.net/spicyj/M6h22/
我已更改Grid
,因此它不会保持自己的状态,而是始终显示通过其道具传递的数据,并在其想要时从其道具中调用onCellChange
请求从其父级更改数据。 (Grid
组件将期望其父级使用修改后的数据更新网格的data
道具。如果父级拒绝(可能是因为数据验证失败或类似),您最终会得到只读网格。)
您还会注意到我创建了一个新的Editor
组件来包装网格及其兄弟按钮。 Editor
组件现在基本上管理整个页面。在一个真实的应用程序中,很可能在其他地方需要网格的内容,因此状态会更高。我删除了您的Button
组件,因为它在本地<button>
标记之外没有做太多工作;我离开了Cell
但也可以将其删除 - Row
可以直接轻松使用<input>
代码。
希望这是有道理的。随意询问是否有任何不清楚的地方。如果您想要更多地了解这些内容,#reactjs IRC room中通常还会有人。
答案 1 :(得分:7)
过去一周左右,我一直在探索ReactJS。我对您的问题的输入是一个新问题:为什么要将Cell组件与Row和Grid组件分开?
来自Backbone背景,Cell&amp; Row&amp; Grid是有意义的,可以对单个Cell Backbone.Views进行精细控制。然而,它看起来像粒度控制&amp; DOM更新是ReactJS试图为您解决的问题,对我来说,有一个Grid组件在其内部实现了一个Row / Cell:
var Grid = React.createClass({
onChange: function(evt, field) {
this.props.data[field] = evt.target.value;
},
render: function () {
var rows = this.state.data.map(function (rowData, index) {
return (
<div className="row" key={index}>
<input value={rowData.name} onChange={this.onChange.bind(null, "name")} />
<input value={rowData.location} onChange={this.onChange.bind(null, "location")} />
<input value={rowData.phone} onChange={this.onChange.bind(null, "phone")} />
</div>
);
});
return <div className="table">
{rows}
</div>;
}
});
(忽略on *更改处理,改进空间。未经测试的代码)
问题是,您是否会在其他地方重复使用Cell或Row作为单独的组件?对我来说,答案是“很可能没有”#34;在这种情况下我的解决方案是有道理的,恕我直言,并摆脱传递数据和更改的问题&amp;下来。
答案 2 :(得分:0)
当父组件没有意义时,在兄弟组件之间共享数据的另一种方法是在组件之间使用事件。例如,您可以使用Backbone.Events,Node.js Emitter移植到浏览器或任何类似的lib。如果你喜欢反应流,你甚至可以使用Bacon.js。这里有一个结合了Bacon.js和React.js的简单例子:http://joshbassett.info/2014/reactive-uis-with-react-and-bacon/