Using React Context with TypeScript

TypeScript takes React's Context API to the next level by safely typing shared state.

When the official Context API released with React 16.3, developers got a powerful new container for application state. If “context” sounds suspiciously global, rest assured: each context must be explicitly referenced by both the Provider that supplies it and the Consumer that puts it to work.

Still, TypeScript is a helpful addition–a statically-verified way to ensure that context instances returned by createContext can only be used as intended.

Using a React Context with TypeScript is much like in JavaScript, but with an both Provider and Consumer restricted to whatever type is assigned to the original createContext call. Here’s a simple example showing a context with a type signature, default value, and (stubbed) callback:

type CounterContext = {
  count: number,
  updateCount(delta: number): void,
};

export default React.createContext<CounterContext>({
  count: 42,
  updateCount: (n) => {
    throw new Error('updateCount() not implemented');
  }
});

While TypeScript has little impact on the implementation, its compile-time guarantees mean less time spent digging into Consumer misbehavior every time a Provider supplies malformed state. That’s more time for adding value to the application, and at very little cost.

So, as long as the types check out, we’re all set to share state through our shiny new CounterContext.

import CounterContext from './context';

const Count: React.SFC<{}> = () => (
  <CounterContext.Consumer>
    {({ count, updateCount }) => (
      <div>
        <h1>Count: {count}</h1>
        <button onClick={() => updateCount(1)}>+1</button>
      </div>
    )}
  </CounterContext.Consumer>
);

class Container extends React.Component<{}, { count: number }> {
  readonly state = { count: 0 };

  increment = (delta: number) => this.setState({
    count: this.state.count + delta
  })

  render() {
    return (
      <CounterContext.Provider
        value={{
          count: this.state.count,
          updateCount: this.increment,
        }}
      >
        <Count />
      </CounterContext.Provider>
    );
  }
}

Find a demo of it all working together in the React with TypeScript repository on Github.