November 26, 2015 · react frontend

Using a proxy for communication between two components in reactjs

This is obsolete. Use mobx or redux for complex state management.

Communication between components is usually done like this.

  1. Child component wants to talk to it's ancesters: In this case, pass a function or states as props down the chain.
  2. Parent component wants to talk to decendents: In this case setState and render decendents again.
  3. Two decendents of a ancester wants to talk to each other: This case is bit tricky, and there's two solutions:

a. pub/sub: The two child components publish/subscribe via a message channel. However if there're multiple instances of the two components. It's hard to differentiat between instances, as they subscribe over the same channel.

b. proxy: messaging via a proxy declared in the ancester. This is the preferred approach. Here's an example.

    Panel --- AnotherPanel
      \           \--------- NewsList
       \----- AnotherPanel
       				\------- NewsDetail

NewsDetail has two buttons. Prev, Next. When clicked, goes to prev and next news item. Also they grayout if it's already the first or last news. So it needs four things from NewsList: hasPrev, hasNext, getPrev, getNext.

First we define a proxy

export class Proxy {
    call(api, ...args) {
        if (api in this) {
            return this[api].apply(null, args);
        }
    }
    implements(def) {
        for (var api in def) {
            if (api == 'call') throw 'Can\'t override "call" function';
            this[api] = def[api];
        }
    }
}

Initialize a proxy inside the ancester. getInitialState is used as constructor, since there's no constructor in React.createClass call.

// panel.jsx
getInitialState() {
  // this function is used as constructor
  this.proxy = new Proxy();
  ...
},
render() {
  return (
    <div>
      <NewsDetail pagingProxy={this.proxy}/>
      <NewsDetail pagingProxy={this.proxy}/>
    </div>
  );
}

NewsList provide implementation in the proxy.

// NewsList.jsx
getInitialState() {
  this.props.pagingProxy.implements({
    hasPrev: (catId, id)=> {
      return this.getIdx(id) != 0;
    },
    hasNext: (catId, id)=> {
      return this.getIdx(id) != this.state.data.length - 1;
    },
    getPrev: (catId, id)=> {
      return this.state.data[this.getIdx(id)-1];
    },
    getNext: (catId, id)=> {
      return this.state.data[this.getIdx(id)+1];
    }
  });
  ...
}

NewsDetail now can call methods in NewsList component.

nextPage() {
  var {catId, id} = this.state;
  var next = this.props.pagingProxy.call('getNext', catId, id);
  if (next) {
  	this.loadNews(catId, next.id);
  }
}

There's catch though. NewsList has to provide implementation to the proxy before NewsDetail uses it. Since component initiazation in react is done in a DFS fashion, declare NewsList before NewsDetail is sufficient to ensure that.

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket