The Problem We're Solving

When building an app you'll end up with many nested components and passing down props can be a pain in the but.

This is usually solved by reaching for a framework like FLUX or Redux.

Context doesn't replace these but can keep you from having to reach for them before you actually need to.

The basic concept

You're setting up a global state that can be pulled into any component.

You start off with creating a Context and a Provider class then use the your context as a provider or consumer.

Dive into Code

Here's the basic setup.

Create your context using React.createContext()

  const DealerContext = React.createContext();

Setup your Provider Class with some default state.

  class DealerProvider extends Component {
  state = {
    name: 'bee Dealer',
    dealerId: 36,
    groupId: 1
  }
  render() {
    return (
      <DealerContext.Provider value={{ state: this.state }}>
        {this.props.children}
      </DealerContext.Provider>
    )
  }
}

Notice we're using our DealerContext.Provider tag to show this is the component that provides context.

Setup your base App component using your new provider.

  class App extends Component {
  render() {
    return (
      <DealerProvider>
        <div className="my-app">
          <AppHeader />
          <DealerPage />
        </div>
      </DealerProvider>
    );
  }
}

export default App;


Now you have the building blocks but how do you get a nested component to consume and use this global state? Well let's build out the DealerPage component and see what it looks like.

This is the DealerPage component that includes two other components but not much else. We've made this a stateless-component because it's main purpose is to include other components and not manage any state. Also notice we're not passing any props here.

  const DealerPage = () => {
  return (
    <div className="dealer-page">
      <DealerProfile />
      <OtherDealerInfo />
    </div>
  )
}

Here's the DealerProfile component referenced in the DealerPage component above. Here we're going to consume data from our context and use that data to display the dealer's name.

  
class DealerProfile extends Component {
  render() {
    return (
      <div className="dealer-profile">
        <DealerContext.Consumer>
          {(context) => (
            <React.Fragment>
              <p>Name: {context.state.name}</p>
              <p>Dealer ID: {context.state.dealerId}</p>
            </React.Fragment>
          )}
        </DealerContext.Consumer>
      </div>
    )
  }
}

Note that the above component is using the same DealerContext component we used while setting up our Provider but now we're calling DealerContext.Consumer to indicate we're consuming information here. Now you have to run a function where the context is passed through and the return value is your JSX code. You'll also see I've used <React.Fragment> here so that I don't have to wrap this information in another div or span tag. This is also new to React.

So to drive a nail through this, we're setting up our Context, setting up a Provider that holds the state, and consuming that state with a Consumer - all without having to pass props from one component to another!

Taking it a step further, you can also add functions to your Provider that can perform like actions. Take this for an easy example:

  class DealerProvider extends Component {
  state = {
    name: 'bee Printing',
    dealerId: 36,
    groupId: 1
  }
  render() {
    return (
      <DealerContext.Provider value={{ 
        state: this.state,
        updateDealerName: (value) => this.setState({ name: value.target.value })
      }}>
        {this.props.children}
      </DealerContext.Provider>
    )
  }
}

class DealerActions extends Component {
  render() {
    return (
      <DealerContext.Consumer>
        {(context) => (
          <input 
            type="text" 
            defaultValue={context.state.name} 
            onChange={context.updateDealerName.bind(this)} />
        )}
      </DealerContext.Consumer>
    )
  }
}

Now we're using the input to update the dealer's name via a function on the Provider.

Wrap it up

I'm still playing and learning this so there's a lot more we could do but it's real exciting and I'm looking forward to using the Context API to get rid of some heavily nested props in apps I've built.

Here's a Pen showing it all together and in action.

See the Pen React Context Demo by Adam Moore (@adamaoc) on CodePen.


1,172 0 12