React is a really great platform for writing applications. With the introduction of React Hooks and JavaScript improvements, writing more sophisticated React applications can be easier. I recently found a nice pattern for sharing state between React components and thought I'd share :)

Let's start with a simple application with a child components:

import Child from './Child';

export default function App(props) {
  return <Child />;
}

The Child components will just expose a button to increment a counter, with state kept in the Child component:

import React, { useState } from 'react';

export default function Child1(props) {
  const [counter, setCounter] = useState(0);
  return <button onClick={() => setCounter(counter + 1)}>
    Child Counter: {counter}
  </button>;
}

This app is pretty simple, just a button that increments a counter:

Click on the button and the counter increments:

Let's say the parent wants to access the counter. This can be accomplished using global state managed by the Child component. The reactn NPM module provides setGlobal and useGlobal methods to make data global. (React allows this via Context, but it's pretty complex to use, so let's use reactn).

Let's change Child to store the counter as a global.

import React, { useEffect } from 'react';
import { useGlobal, setGlobal } from 'reactn';

setGlobal({});

export default function Child1(props) {
  const [child, setChild] = useGlobal('child');
  useEffect(() => (child ? true : setChild({ ...child, counter: 0 })), [child, setChild]);
  return <button onClick={() => setChild({ ...child, counter: child?.counter + 1 })}>Child Counter: {child?.counter}</button>;
}

This code has the same output as before, except the counter is now available as a global. Note, useEffect is needed to initialize the child global object. The notation I'm using is pretty terse to keep it compact, it's just initialization code so let's minimize it.

Note the use of child?.counter. The ?. operator is a new JavaScript operator that is very handy. The first time Child1 is rendered child will be undefined. The ?. short circuits evaluation so the counter member isn't accessed to prevent a run time error... caused by attempting to accessundefined.counter.

Now, let's use this counter in another component. For simplicity we'll just access it from the parent component. It is just as easy to access from components that are peers or children of the Child component.

import { useGlobal } from 'reactn';
import Child from './Child';

export default function App(props) {
  const [child] = useGlobal('child');
  return (
    <div>
      <div>Parent Value: {child?.counter ? child.counter : 'NA'}</div>
      <Child />
    </div>
  );
}

child will be undefined until the button is clicked, so the initial screen looks like this:

Click on the button to increment the counter in the child and update it in the parent:

This looks pretty simple, and it is. Using Hooks and reactn decreases the amount of code required to this by at least 80%. The only tricky business is handling the race condition caused by the parent component being mounted before the child component it uses. Luckily, the awesome ?. is now available to minimize the code required to deal with stuff like undefined, which is a fleeting condition anyway.