373

I just got started with ReactJS and am a little stuck on a problem that I have.

My application is essentially a list with filters and a button to change the layout. At the moment I'm using three components: <list />, < Filters /> and <TopBar />, now obviously when I change settings in < Filters /> I want to trigger some method in <list /> to update my view.

How can I make those 3 components interact with each other, or do I need some sort of global data model where I can just make changes to?

Liam
  • 27,717
  • 28
  • 128
  • 190
woutr_be
  • 9,532
  • 24
  • 79
  • 129
  • 2
    Are all three sibling components or is one within the other? – pgreen2 Jan 23 '14 at 17:25
  • 1
    They're all three components, I've already re-arranged my application so that they now all have the same parent who can provide them with data. – woutr_be Jan 24 '14 at 01:00
  • 5
    This is where you could use flux or pubsub pattern. Based on the docs in react docs they leave a somewhat ambiguous sentence: "For communication between two components that don't have a parent-child relationship, you can set up your own global event system." http://facebook.github.io/react/tips/communicate-between-components.html – BingeBoy Mar 03 '15 at 20:39
  • 1
    @BingeBoy is right [Flux](https://github.com/facebook/flux) is great way of writing reactjs apps, that can handle the problem of data flow, data sharing by many components. – Ankit Patial Oct 10 '15 at 17:32
  • All questions when you read a question like this, are "do your components have parent-child relationship". Wonder how React creators were half-sighted not to see this obvious high-usage scenario. – Green Dec 17 '15 at 02:59
  • check this technique here. http://stackoverflow.com/questions/35649635/communication-between-reactjs-components – Khurram Ijaz Feb 26 '16 at 11:46
  • http://stackoverflow.com/questions/35649635/communication-between-reactjs-components – Khurram Ijaz Feb 26 '16 at 11:46
  • Add code examples. – Black Aug 11 '16 at 09:38
  • 5
    If you don't want to get into Flux or Redux, this is an awesome article on this topic http://andrewhfarmer.com/component-communication/ – garajo Aug 26 '16 at 08:31
  • @garajo seems that link is broken now, but i think this is the content has been moved here: https://www.javascriptstuff.com/component-communication/ – Gabriel Grant Mar 25 '21 at 01:55
  • You need a global state management in react such as Redux. It's also the most popular lib in [React State Management Libraries](https://fe-tool.com/en-us/awesome-react-state-management) list. – cwtuan Dec 26 '22 at 13:05

11 Answers11

350

The best approach would depend on how you plan to arrange those components. A few example scenarios that come to mind right now:

  1. <Filters /> is a child component of <List />
  2. Both <Filters /> and <List /> are children of a parent component
  3. <Filters /> and <List /> live in separate root components entirely.

There may be other scenarios that I'm not thinking of. If yours doesn't fit within these, then let me know. Here are some very rough examples of how I've been handling the first two scenarios:

Scenario #1

You could pass a handler from <List /> to <Filters />, which could then be called on the onChange event to filter the list with the current value.

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);

Scenario #2

Similar to scenario #1, but the parent component will be the one passing down the handler function to <Filters />, and will pass the filtered list to <List />. I like this method better since it decouples the <List /> from the <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);

Scenario #3

When the components can't communicate between any sort of parent-child relationship, the documentation recommends setting up a global event system.

Michael LaCroix
  • 5,780
  • 1
  • 24
  • 13
  • Thanks, scenario #2 makes a lot of sense, and after thinking about it yesterday I re-structured my application to work exactly like that. It feels a bit strange since all components now rely on each other. But it seems to do the job. – woutr_be Jan 24 '14 at 01:02
  • 8
    The nice thing with #2 is that they only rely on a parent who passes a prop to each component: a function as `updateFilter` to `` and an array as `items` to ``. You could use those child components in other parents with different behaviors, either together or solo. For example, if you wanted to display a dynamic list but didn't need filtering. – Michael LaCroix Jan 24 '14 at 01:10
  • The only issue that I have is with passing functions as parameters, that makes them rely on each other. Wouldn't it be better for the child components to send out events. That way they only rely on data that they get from their parents. – woutr_be Jan 24 '14 at 01:52
  • 3
    @woutr_be Not sure if it would fit into your requirement but when in a similar situation sometime back, we made use of following two functions to sort of communicate between the child and parent components:- listenTo: function(eventName, eventCallback) { $(window.document).bind(eventName,eventCallback);} triggerEvent: function(eventName, params) { $.event.trigger(eventName, params);} Hope it helps! (sorry could not format it better) – 5122014009 Jan 24 '14 at 05:08
  • @Sayuri Thanks, it's something like that I was looking for, I know ReactJS has it's own build in event system. – woutr_be Jan 24 '14 at 06:44
  • 39
    For scenario 3, is there a recommended approach? Any docs or examples on this by generating custom synthetic events? I have not found anything in the main docs. – pwray Jan 24 '14 at 11:23
  • Don't forget the `key` attribute when rendering components in a loop. Inside `List`'s render, each `
  • ` should be `
  • {item}
  • `. – Ross Allen Jun 05 '14 at 15:03
  • @PaulWray You could use something similar to [`Backbone.Events`](http://backbonejs.org/#Events) for a global dispatcher. `var Dispatcher = {}; _.extend(Dispatcher, Backbone.Events);` – Ross Allen Jun 05 '14 at 15:07
  • @pwray Yes there is, take a look at Flux. It is storing app state globally and conveniently so that you can render your components as a function of that app state. Need to communicate between 2 components, just define a new attribute on a Flux store, and modify that attribute using Events in a one way flow of data. There are many Flux implementations, I suggest redux, as it is the simplest implementation that also has some good design patterns from functional programming, such as immutability. – Kemal Dağ Dec 17 '15 at 15:31
  • Yes Ive ben using reflux version of flux for a while now, and looking into redux now. – pwray Dec 22 '15 at 03:46
  • 2
    Scenario #2 *makes* a lot of sense... *until* you have to jeopardize design (if only, Layout) -- then you realize the necessity for an EventHub/PubSub. – Cody May 16 '16 at 18:31
  • Dude you are a savior – Vishnu Kiran Mar 13 '18 at 17:18
  • 20
    The scenario #3 link is dead and redirects to an unrelated React docs page now. – beporter Oct 02 '19 at 21:36
  • For scenario #3, use redux or any of its alternatives @beporter. – Galilo Galilo May 26 '21 at 11:42