gintas.io

React Context

February 03, 2019

React context API lets you pass data down the component tree without using props.

Step 1

Start by creating your context provider.

import React from 'react'

export const ThemeContext = React.createContext()

export class ThemeProvider extends React.Component {
  state = { primary: 'teal', secondary: 'tan' }

  changePrimary = color => {
    this.setState({ primary: color })
  }

  changeSecondary = color => {
    this.setState({ secondary: color })
  }

  render() {
    return (
      <ThemeContext.Provider
        value={{
          ...this.state,
          changePrimary: this.changePrimary,
          changeSecondary: this.changeSecondary
        }}
      >
        {this.props.children}
      </ThemeContext.Provider>
    )
  }
}

export const ThemeConsumer = ThemeContext.Consumer
import React from 'react'

export const ContentContext = React.createContext()

export class ContentProvider extends React.Component {
  state = { header: "Header", text: "Some content bla bla bla." }

  render() {
    return (
      <ContentContext.Provider value={{...this.state}}>
        {this.props.children}
      </ContentContext.Provider>
    )
  }
}

export const ContentConsumer = ContentContext.Consumer

Step 2

Wrap necessary components that will use context with a Provider.

<ThemeProvider>
  <ContentProvider>
    <ColorInputs />
    <ColorDisplay />
  </ContentProvider>
</ThemeProvider>

Step 3

If you need to access a single context, you can use static contextType = Context after which it will be available under this.context.

import React from 'react'
import { ThemeContext } from './ThemeContext'

class ColorInputs extends React.Component {
  static contextType = ThemeContext

  render() {
    return (
      <React.Fragment>
        <label>Primary</label>
        <input
          type='text'
          placeholder='primary'
          onChange={e => this.context.changePrimary(e.target.value)}
        />
        <label>Secondary</label>
        <input
          type='text'
          placeholder='secondary'
          onChange={e => this.context.changeSecondary(e.target.value)}
        />
      </React.Fragment>
    )
  }
}

export default ColorInputs

If you need to access multiple contexts, then wrapping with a consumer is necessary. Although, you can still import a single context with static contextType.

import React from 'react'
import { ThemeContext } from './ThemeContext'
import { ContentConsumer } from './ContentContext'

class ColorDisplay extends React.Component {
  static contextType = ThemeContext

  render() {
    return (
      <div>
        <ContentConsumer>
          {content => (
            <React.Fragment>
              <h3>{content.header}</h3>
              <label>{content.text}</label>
            </React.Fragment>
          )}
        </ContentConsumer>
        <div
          style={{
            display: 'inline-block',
            backgroundColor: this.context.primary,
            width: 100,
            height: 100
          }}
        >
          Primary
        </div>
        <div
          style={{
            display: 'inline-block',
            backgroundColor: this.context.secondary,
            width: 100,
            height: 100
          }}
        >
          Secondary
        </div>
      </div>
    )
  }
}

export default ColorDisplay

Gintas Skersys

Blog by Gintas Skersys
I store my notes and project videos.