我刚开始使用ReactJS,我有一个问题。

我的应用程序本质上是一个带有过滤器和更改布局的按钮的列表。 目前我正在使用三个组件:<list />, < Filters />和<TopBar />,现在显然,当我在< Filters />中更改设置时,我想在<list />中触发一些方法来更新我的视图。

我如何使这3个组件相互交互,或者我是否需要某种全局数据模型,我只需要对其进行更改?


当前回答

我曾经是你现在的位置,作为一个初学者,你有时会觉得不适应如何反应的方式来做这件事。我将尝试用我现在思考的方式来解决这个问题。

国家是交流的基石

通常它归结于你改变这个组件的状态的方式在你的例子中你指出三个组件。

<List />:它可能会根据筛选器显示项目列表 <Filters />:筛选将更改数据的选项。 <TopBar />:选项列表。

为了协调所有这些交互你需要一个更高的组件我们叫它App,它会把动作和数据传递给每个组件例如,它看起来是这样的

<div>
  <List items={this.state.filteredItems}/>
  <Filter filter={this.state.filter} setFilter={setFilter}/>
</div>

因此,当setFilter被调用时,它将影响filteredItem并重新渲染两个组件;。如果这不是完全清楚,我给你一个例子,你可以在一个文件中检查复选框:

import React, {Component} from 'react';
import {render} from 'react-dom';

const Person  = ({person, setForDelete}) => (
          <div>
            <input type="checkbox" name="person" checked={person.checked} onChange={setForDelete.bind(this, person)} />
            {person.name}
          </div>
);


class PeopleList extends Component {

  render() {

    return(
      <div>
       {this.props.people.map((person, i) => {
         return <Person key={i} person={person} setForDelete={this.props.setForDelete} />;
       })}
       <div onClick={this.props.deleteRecords}>Delete Selected Records</div>
     </div>
    );
  }

} // end class

class App extends React.Component {

  constructor(props) {
    super(props)
    this.state = {people:[{id:1, name:'Cesar', checked:false},{id:2, name:'Jose', checked:false},{id:3, name:'Marbel', checked:false}]}
  }

  deleteRecords() {
    const people = this.state.people.filter(p => !p.checked);

    this.setState({people});
 }

  setForDelete(person) {
    const checked = !person.checked;
    const people = this.state.people.map((p)=>{
      if(p.id === person.id)
        return {name:person.name, checked};
      return p;
    });

    this.setState({people});
  }

  render () {

    return <PeopleList people={this.state.people} deleteRecords={this.deleteRecords.bind(this)} setForDelete={this.setForDelete.bind(this)}/>;
  }
}

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

其他回答

最好的方法取决于您计划如何安排这些组件。下面是我现在想到的几个例子:

<Filters />是<List />的子组件 <Filters />和<List />都是父组件的子组件 <Filters />和<List />完全存在于单独的根组件中。

可能还有其他我没有想到的情况。如果你的不符合这些要求,请告诉我。以下是我处理前两种情况的一些非常粗略的例子:

场景# 1

您可以将处理程序从<List />传递到<Filters />,然后可以在onChange事件上调用该处理程序以使用当前值筛选列表。

JSFiddle for #1 →

/** @jsx React.DOM */

var Filters = React.createClass({
  handleFilterChange: function() {
    var value = this.refs.filterInput.getDOMNode().value;
    this.props.updateFilter(value);
  },
  render: function() {
    return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
  }
});

var List = React.createClass({
  getInitialState: function() {
    return {
      listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
      nameFilter: ''
    };
  },
  handleFilterUpdate: function(filterValue) {
    this.setState({
      nameFilter: filterValue
    });
  },
  render: function() {
    var displayedItems = this.state.listItems.filter(function(item) {
      var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
      return (match !== -1);
    }.bind(this));

    var content;
    if (displayedItems.length > 0) {
      var items = displayedItems.map(function(item) {
        return <li>{item}</li>;
      });
      content = <ul>{items}</ul>
    } else {
      content = <p>No items matching this filter</p>;
    }

    return (
      <div>
        <Filters updateFilter={this.handleFilterUpdate} />
        <h4>Results</h4>
        {content}
      </div>
    );
  }
});

React.renderComponent(<List />, document.body);

场景# 2

与场景#1类似,但是父组件将把处理程序函数传递给<Filters />,并将过滤后的列表传递给< list />。我更喜欢这个方法,因为它将<List />与<Filters />解耦。

JSFiddle for #2 →

/** @jsx React.DOM */

var Filters = React.createClass({
  handleFilterChange: function() {
    var value = this.refs.filterInput.getDOMNode().value;
    this.props.updateFilter(value);
  },
  render: function() {
    return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
  }
});

var List = React.createClass({
  render: function() {
    var content;
    if (this.props.items.length > 0) {
      var items = this.props.items.map(function(item) {
        return <li>{item}</li>;
      });
      content = <ul>{items}</ul>
    } else {
      content = <p>No items matching this filter</p>;
    }
    return (
      <div className="results">
        <h4>Results</h4>
        {content}
      </div>
    );
  }
});

var ListContainer = React.createClass({
  getInitialState: function() {
    return {
      listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
      nameFilter: ''
    };
  },
  handleFilterUpdate: function(filterValue) {
    this.setState({
      nameFilter: filterValue
    });
  },
  render: function() {
    var displayedItems = this.state.listItems.filter(function(item) {
      var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
      return (match !== -1);
    }.bind(this));

    return (
      <div>
        <Filters updateFilter={this.handleFilterUpdate} />
        <List items={displayedItems} />
      </div>
    );
  }
});

React.renderComponent(<ListContainer />, document.body);

场景# 3

当组件不能在任何类型的父子关系之间进行通信时,文档建议设置一个全局事件系统。

即使他们不是父母-孩子关系,也有这样的可能性——那就是流动。有一个很好的(对我个人来说)实现称为Alt.JS(与Alt-Container)。

例如,您可以拥有依赖于组件详细信息设置的边栏。Component Sidebar与SidebarActions和SidebarStore连接,而Details是DetailsActions和DetailsStore。

你可以像那样使用AltContainer

<AltContainer stores={{
                    SidebarStore: SidebarStore
                }}>
                    <Sidebar/>
</AltContainer>

{this.props.content}

这将保留商店(我可以用“商店”而不是“商店”道具)。现在,{this.props。content}可以是细节,取决于路由。假设/Details将我们重定向到那个视图。 例如,细节将有一个复选框,如果选中的话,将把侧边栏元素从X更改为Y。

从技术上讲,它们之间没有关系,没有通量是很难做到的。但这样就相当容易了。

现在让我们进入DetailsActions。我们会在那里创建

class SiteActions {
constructor() {
    this.generateActions(
        'setSiteComponentStore'
    );
}

setSiteComponent(value) {
    this.dispatch({value: value});
}
}

和DetailsStore

class SiteStore {
constructor() {
    this.siteComponents = {
        Prop: true
    };

    this.bindListeners({
        setSiteComponent: SidebarActions.COMPONENT_STATUS_CHANGED
    })
}

setSiteComponent(data) {
    this.siteComponents.Prop = data.value;
}
}

现在,这就是魔法开始的地方。

如你所见,这里有SidebarActions的bindListener。如果使用setSiteComponent,将使用ComponentStatusChanged。

现在在SidebarActions

    componentStatusChanged(value){
    this.dispatch({value: value});
}

我们有这样的东西。它会在调用时分派那个对象。如果setSiteComponent in store将被使用(你可以在组件中使用例如onChange on Button或其他)它将被调用

现在在边栏商店中我们会有

    constructor() {
    this.structures = [];

    this.bindListeners({
        componentStatusChanged: SidebarActions.COMPONENT_STATUS_CHANGED
    })
}

    componentStatusChanged(data) {
    this.waitFor(DetailsStore);

    _.findWhere(this.structures[0].elem, {title: 'Example'}).enabled = data.value;
}

这里你可以看到,它会等待DetailsStore。这是什么意思?这或多或少意味着这个方法需要等待DetailsStoreto更新才能更新自己。

博士tl; 一个商店正在监听商店中的方法,并将从组件操作中触发一个操作,该操作将更新自己的商店。

我希望它能对你有所帮助。

如果您想探索组件之间通信的选项,并且感觉通信变得越来越困难,那么您可能会考虑采用一种好的设计模式:Flux。

它只是一组规则,定义了如何存储和更改应用程序范围内的状态,以及如何使用该状态来呈现组件。

Flux有很多实现,Facebook的官方实现就是其中之一。虽然它被认为包含了最多的样板代码,但它更容易理解,因为大部分内容都是显式的。

其他的一些替代品是flumox, flumox和redux。

好吧,有一些方法可以做到这一点,但我只想专注于使用Redux使用商店,这让你的生活在这些情况下变得更容易,而不是给你一个快速的解决方案,只有在这种情况下,使用纯React最终会在真正的大型应用程序中混乱,组件之间的通信变得越来越难,随着应用程序的增长…

那么Redux能为你做什么呢?

Redux就像应用程序中的本地存储,当你需要在应用程序的不同位置使用数据时,可以使用它。

基本上,Redux的想法最初来自于flux,但有一些根本性的变化,包括通过只创建一个存储来拥有一个真相来源的概念……

看看下面的图表,看看Flux和Redux之间的一些差异…

如果您的应用程序需要组件之间的通信,请考虑从一开始就在应用程序中应用Redux。

另外,阅读Redux文档中的这些词可能会有帮助:

As the requirements for JavaScript single-page applications have become increasingly complicated, our code must manage more state than ever before. This state can include server responses and cached data, as well as locally created data that has not yet been persisted to the server. UI state is also increasing in complexity, as we need to manage active routes, selected tabs, spinners, pagination controls, and so on. Managing this ever-changing state is hard. If a model can update another model, then a view can update a model, which updates another model, and this, in turn, might cause another view to update. At some point, you no longer understand what happens in your app as you have lost control over the when, why, and how of its state. When a system is opaque and non-deterministic, it's hard to reproduce bugs or add new features. As if this wasn't bad enough, consider the new requirements becoming common in front-end product development. As developers, we are expected to handle optimistic updates, server-side rendering, fetching data before performing route transitions, and so on. We find ourselves trying to manage a complexity that we have never had to deal with before, and we inevitably ask the question: is it time to give up? The answer is no. This complexity is difficult to handle as we're mixing two concepts that are very hard for the human mind to reason about: mutation and asynchronicity. I call them Mentos and Coke. Both can be great in separation, but together they create a mess. Libraries like React attempt to solve this problem in the view layer by removing both asynchrony and direct DOM manipulation. However, managing the state of your data is left up to you. This is where Redux enters. Following in the steps of Flux, CQRS, and Event Sourcing, Redux attempts to make state mutations predictable by imposing certain restrictions on how and when updates can happen. These restrictions are reflected in the three principles of Redux.

我看到这个问题已经有了答案,但如果你想了解更多细节,组件之间的通信总共有3种情况:

案例1:父到子通信 案例2:子到父通信 案例3:不相关的组件(任何组件到任何组件)通信