在React.js中编辑丰富的数据结构

我正在尝试为数据结构创build一个简单的基于网格的编辑器,而且我还有一些React.js的概念问题。 他们的文档对此没有太大的帮助,所以我希望有人能帮上忙。

首先,从外部组件向内部组件转移状态的正确方法是什么? 是否有可能在内部组件的状态变化“冒泡”到外部组件?

其次,两个单独的组件可以共享数据,以便在另一个中可以看到一个突变?

下面是我想做的事情( JSFiddle版本 )的一个简单的例子:

我有一个company对象包含一个employee对象的数组。 我想在可编辑的网格中列出employee列表。 当我点击button时,我想看到生成的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') ); 

我认为这是目前React中最缺乏文档的部分。 组件之间build议的通信方式是简单地在父母之间进行通信时设置道具,当从小孩到父母通信时通过道具传递callback。

如果您觉得您想在兄弟组件之间共享数据,则意味着应该有一个父组件pipe理状态并将其传递给这两个组件。 大多数情况下,你的状态应该靠近你的组件层次结构的顶部,每个信息都应该(最多)一个组件的状态,而不是更多。

想了解更多,请参阅Pete Hunt的博客文章Thinking in React


考虑到这一点,我已经更新了你的小提琴:

http://jsfiddle.net/spicyj/M6h22/

我已经改变了Grid所以它不保持自己的状态,而是总是显示通过它的道具传递的数据,并且当它想要从其父项请求更改数据时,从它的道具上调用onCellChange 。 ( Grid组件会希望它的父节点用修改后的数据更新网格的data支撑,如果父节点拒绝(也许是因为数据validation失败或者类似的原因),那么你最终会得到一个只读网格。

您还会注意到我创build了一个新的Editor组件来包装网格及其兄弟button。 Editor组件现在基本上pipe理整个页面。 在一个真正的应用程序中,网格的内容很可能在其他地方被需要,所以状态会变得更高。 我删除了你的Button组件,因为它没有超出本地的<button>标签; 我离开了Cell但它也可以被删除 – Row可以很容易地直接使用<input>标签。

希望这是有道理的。 随意问,如果有什么不清楚的。 #REactjs IRC房间里通常也有人,如果你想聊聊更多关于这个的话。

过去一周左右我一直在探索ReactJS。 我对你的问题的input是问一个新的问题:为什么你将单元格从行和网格组件分开?

从骨干背景来看,Cell&Row&Grid是有意义的,可以精确控制单个Cell Backbone.Views。 然而,ReactJS试图为你解决这个细粒度的控制和DOM更新似乎是这样的,对于我来说,有一个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>; } }); 

(忽略*改变处理,在那里改进的空间。未经testing的代码)

问题是,你是否会重复使用Cell或Row作为其他地方的单个组件? 对我来说,答案是“很可能不是”。 在这种情况下,我上面的解决scheme是有道理的,恕我直言,摆脱了传递数据和上下变化的问题。

在父组件之间没有意义的情况下,在兄弟组件之间共享数据的另一种方式是在组件之间使用事件。 例如,您可以使用Backbone.Events,Node.js发射器移植到浏览器或任何类似的库。 你甚至可以使用Bacon.js,如果你喜欢反应stream。 Bacon.js和React.js结合在一起,有一个非常简单的例子: http ://joshbassett.info/2014/reactive-uis-with-react-and-bacon/