react-flux app

时间:2015-04-29 22:44:54

标签: javascript reactjs reactjs-flux flux

问题:

是否有任何方法可以使用标准助焊剂工作流程 - 在组件内部使用操作商店,并且仍然可以将此组件用于多种不同目的,或者如果没有,是否有任何方法可以在flux-react应用程序中使用复杂的嵌套结构,而不会通过巨大的回调管道传播每个更改?

示例(如果问题不够明确):

假设我有一些超级简单的自定义组件,例如 ToggleButton Slider DatePicker 等等。它们需要可重用,所以我不能使用它们内部的任何动作,而是我定义了回调函数。例如, DatePicker 上的onChange会像这样触发:

this.props.onChange(data);

我有一个自定义组件,可以将其称为 InfoBox ,其中包含上述几个简单组件。该组件侦听每个子组的更改,如下所示:

<DatePicker ref='startDate' onChange={this.startDate_changeHandler} />

InfoBox 用于不同的目的,所以我猜它也不能绑定到特定的商店。

我还有一个自定义 Grid 组件,可以呈现 InfoBox 的许多实例。此网格用于在不同页面上显示不同的数据,每个页面可以有多个网格 - 所以我认为我不能将它与动作和商店绑定。

现在这里一切都变得疯狂,忍受我 - 我有几页 - 客户产品文章等..每个网格都至少有一个网格,每个网格都有一些过滤器(如搜索)。

页面肯定可以使用动作和存储,但页面之间有很大的相似之处,我不想复制那么多代码(不仅是方法,还有标记)。

正如您可能看到它的结构非常复杂,在我看来,对于DataPicker > InfoBox > Grid > Page > Something else这样的嵌套组件中的每个更改实现回调方法的管道线是不对的。

1 个答案:

答案 0 :(得分:3)

您完全正确的做法是更改DatePicker组件中的日期不应触发Flux操作。 Flux操作用于更改应用程序状态,并且几乎从不查看视图状态意味着“输入框X包含值Z”或“列表Y已折叠”的状态。

很高兴您正在创建可重用的组件,例如Grid等,它可以帮助您使应用程序更易于维护。

解决问题的方法是将组件从顶层传递到底层。这可以通过子组件或简单的道具来完成。

假设你有一个页面,它显示了两个网格,一个网格 - 比方说 - 会议约会和一个带有待办事项的网格。现在,层次结构中的页面本身太高,无法知道何时触发操作,而GridInfoBox过于笼统,无法知道要触发的操作。你可以像你说的那样使用回调,但这可能有点太局限了。

所以你有一个页面,你有一系列约会和一系列待办事项。要渲染并连接它,你可能会有这样的东西:

var TodoActions = {
  markAsComplete: function (todo) {
    alert('Completed: ' + todo.text);
  }
};

var InfoBox = React.createClass({
  render: function() {
    return (
      <div className="infobox">
        {React.createElement(this.props.component, this.props)}
      </div>
    );
  }
});

var Grid = React.createClass({
  render: function() {
    var that = this;
    return (
      <div className="grid">
        {this.props.items.map(function (item) {
          return <InfoBox component={that.props.component} item={item} />;
        })}
      </div>
    );
  }
});

var Todo = React.createClass({
  render: function() {
    var that = this;
    return (
      <div>
        Todo: {this.props.item.text}
        <button onClick={function () { TodoActions.markAsComplete(that.props.item); }}>Mark as complete</button>
      </div>
    );
  }
});

var MyPage = React.createClass({
  getInitialState: function () {
    return {
      todos: [{text: 'A todo'}]
    };
  },
  render: function() {
    return (
      <Grid items={this.state.todos} component={Todo} />
    );
  }
});

React.render(<MyPage />, document.getElementById('app'));

如您所见,GridInfoBox都知道的很少,除了一些数据传递给它们,并且它们应该在底部呈现一个知道如何触发动作的组件。 InfoBox也会将其所有道具传递给Todo,这会将Todo todo对象传递给InfoBox

所以这是处理这些事情的一种方法,但它仍然意味着你要在组件之间传播道具。在某些情况下,如果您有深度嵌套,那么传播会变得乏味,并且很容易忘记添加它会使组件进一步向下破坏。对于这些情况,我建议您查看React中的上下文,这非常棒。以下是对上下文的一个很好的介绍:https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html

修改

更新并回复您的评论。为了在示例中概括Todo以便它不知道显式调用哪个动作,可以将它包装在一个知道的新组件中。

这样的事情:

var Todo = React.createClass({
  render: function() {
    var that = this;
    return (
      <div>
        Todo: {this.props.item.text}
        <button onClick={function () { this.props.onCompleted(that.props.item); }}>Mark as complete</button>
      </div>
    );
  }
});

var AppointmentTodo = React.createClass({
  render: function() {
    return <Todo {...this.props} onCompleted={function (todo) { TodoActions.markAsComplete(todo); }} />;
  }
});

var MyPage = React.createClass({
  getInitialState: function () {
    return {
      todos: [{text: 'A todo'}]
    };
  },
  render: function() {
    return (
      <Grid items={this.state.todos} component={AppointmentTodo} />
    );
  }
});

因此,它不会让MyPageTodo传递给Grid,而是传递AppointmentTodo,它只作为知道特定操作的包装组件,释放Todo 1}}只关心渲染它。这是React中非常常见的模式,您可以在其中将组件委托给另一个组件,并将道具传递给它。