/ react

Sharing Data Between React Components

Developers new to React are usually overwhelmed by the variety of state management frameworks that are added to the library. I think there's a simple pattern they all share Understanding that pattern would make it all seem much more simple.

Shared Data Via A Common Parent

The React data management model is analogue to lexical function scope. We can imagine the component tree as a hierarchy of blocks, and data is saved in the innermost block that requires it.

As an example let's consider a component that shows a text string and another one that shows the test string's size:

See the Pen qxXRbQ by Ynon Perek (@ynonp) on CodePen.

Naturally all 3 components could have been merged to a single one, so let's look at this situation as representing many other which are more complex.

The model is fairly easy to understand. The three components above are illustrated as:

<App>
    <Editor />
    <CharacterCount />
</App>

And since both Editor and CharacterCount work on the same mutable data (the text), this data is saved at a higher level: The App component.

As the application grows you'll find this pattern doesn't scale well. Passing the state and its modifier methods down the components chain is cumbersome, especially when they need to be passed through dozens of components.

That's the problem state management frameworks address.

Flux In A Nutshell

In programming the alternative to lexical scope are global variables. A global variable is saved somewhere outside the lexical scope of the function and can be accessed by any function or block. Well you just described flux.

The first pattern facebook had for sharing data outside the components hierarchy was to save the data in an external singleton object. That object can be modified and accessed from every component in the application.

This is the place state management frameworks begin to diverge. Some will have just one singleton object for all the data, some will split the data to many singleton objects. Some will allow direct access to this data, while others reuqire you to go through a mediator layer of some sort. But they all share the same idea that state is saved externally to the components tree and available to each component regardless of its position in the tree.

Let's build a simple DIY flux to understand its dynamics. A first naive attempt may look like this:

let _text = 'Hello World';

function Editor(props) {
  return (
    <input type='text' value={_text} onChange={e => { _text = e.target.value; } } />
  );
}

function CharacterCount(props) {
  return <span>{_text.length}</span>;
}

But if you try to run this live you'll find changing the value in the text box has no effect on the page. The reason is the components don't "know" an external data they depend on has changed, and so won't re-render themselves. Obviously a component will only re-render when either its state or props change.

The solution is to add another layer of abstraction that will control our access to _text and will notify everyone when the value changes.

The components themselves should also be modified: It was a mistake to use external data in render, because when it changes the element won't re-render. Instead we'll save the external data into state every time it changes, and use the state as our input to render.

The modified (fixed) version of the code looks like this:

See the Pen aqyJNo by Ynon Perek (@ynonp) on CodePen.

Summing Up

Saving shared application data in an external data structure is the main idea state management frameworks share. Each framework has its own mechanics of how this data is accessed and modified, and that's ok. Eventually you'll have to find the framework that works best for you.

One noticable difference between the code presented here and real world framework code is the code repetition you saw when working with external data. The pattern of a React component reading external data into its state, and using it as an input to render is very popular. It's also usually written for you by the framework authors: In mobx you'll apply that functionality with the observer function, in react-redux it's called connect and in reflux you get it by extending Reflux.Component. Either way the pattern is the same: External data turns to state and used in render.

Since state management frameworks in React all solve the same problem and in more or less the same way, it's helpful not to get too emotionally attached to one or the other. Select the one that resonates with you best. There's no wrong answer here.