46

我正在尝试为数据结构创建一个简单的基于网格的编辑器,但我在使用 React.js 时遇到了一些概念问题。他们的文档对此不是很有帮助,所以我希望这里有人可以提供帮助。

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

其次,两个独立的组件是否可以共享数据,以便一个中的突变在另一个中可见?

下面是我想做的事情的简化示例(JSFiddle 版本):

我有一个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')
);
4

3 回答 3

52

我认为这是目前 React 中文档最不充分的部分。建议的组件间通信方式是在父子通信时简单地设置 props,在子子通信时通过 props 传递回调。

当您觉得要在兄弟组件之间共享数据时,这意味着应该有一个父组件来管理状态并将其传递给两个组件。大多数时候,您的状态应该位于组件层次结构的顶部附近,并且每条信息应该(最多)位于一个组件的状态中,而不是更多。

有关这方面的更多信息,请参阅 Pete Hunt 的博客文章Thinking in React


考虑到这一点,我更新了你的fiddle

我进行了更改Grid,使其不保持自己的状态,而是始终显示通过其 props 传递的数据,并onCellChange在它想要从其父级请求更改数据时从其 props 调用。Grid组件将期望其父data级使用修改后的数据更新网格的道具。如果父级拒绝(可能是由于数据验证失败或类似原因),您最终会得到一个只读网格。)

您还会注意到我创建了一个新Editor组件来包装网格及其同级按钮。该Editor组件现在基本上管理整个页面。在一个真实的应用程序中,网格的内容很可能在其他地方需要,因此状态会被移动到更高的位置。我删除了您的组件,因为它在本机标签Button之外没有做太多事情;<button>我离开了Cell,但它也可以被删除——Row可以很容易地直接使用<input>标签。

希望这是有道理的。随意询问是否有任何不清楚的地方。#reactjs IRC 房间里通常也有人,如果你想更多地聊聊这些。

于 2013-12-27T06:57:11.877 回答
7

过去一周左右我一直在探索 ReactJS。我对您的问题的输入是提出一个新问题:为什么要将 Cell 组件与 Row 和 Grid 组件分开?

来自 Backbone 背景,Cell & Row & Grid 是有意义的,可以对单个 Cell Backbone.Views 进行精细控制。然而,粒度控制和 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*Change 处理,那里有改进的余地。未经测试的代码)

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

于 2014-04-08T03:03:22.283 回答
0

当父组件没有意义时,在兄弟组件之间共享数据的另一种方法是在组件之间使用事件。例如,您可以使用 Backbone.Events、Node.js Emitter 移植到浏览器或任何类似的库。如果您更喜欢反应式流,您甚至可以使用 Bacon.js。这里有一个结合 Bacon.js 和 React.js 的简单示例:http: //joshbassett.info/2014/reactive-uis-with-react-and-bacon/

于 2014-06-04T16:02:25.323 回答