Skip to content

Instantly share code, notes, and snippets.

@wktdev
Last active December 21, 2021 20:57
Show Gist options
  • Save wktdev/e320d78f9a1b8e28e8bdede54f19746e to your computer and use it in GitHub Desktop.
Save wktdev/e320d78f9a1b8e28e8bdede54f19746e to your computer and use it in GitHub Desktop.

The Context API

The Context API allows you to reference state set on a higher level component from a lower level component. The Context API replaces "prop drilling" as a simpler way to reference state from nested components.

Create a Provider

Set a component named Provider (technically you can name it anything) at the top level component of your app. Provider is the name that is conventionally used for this component.


class App extends Component {
  render() {
    return (
      <Provider>

      </Provider>
    );
  }
}

Write the provider component and include a state property. In the return statement write the boiler plate code shown below.

class Provider extends Component {
  state = {
    name: 'Bob',
    age: 33
  }
  render() {
    return (
      <Context.Provider value={{      
        state: this.state
      }}>


      {this.props.children}
      </Context.Provider>
    )
  }
}

// Top level

class App extends Component {
  render() {
    return (
      <Provider>
        
        
      
      </Provider>
    );
  }
}

You have sucessfully written basic React Context boiler plate code. The code below is an example of how to write a component to use the state declared in the Provider. Look at the Person component below. In the return statement encapsulate your code in <Context.Consumer> </Context.Consumer>. <Context.Consumer> gives the component access to the state that is set on the Provider. Between the Consumer opening and closing tags, encapsulate the state you want to reference inside a function (the function is called a "render props function".


class Person extends Component {
  render() {
    return (

        <Context.Consumer>
          {(context) => (                           /*     <----"render props" function  */
               
            <div>

              <p>Name: {context.state.name}</p>    {/*     <--- state     */}

            </div>

    
          )}
        </Context.Consumer>
 
    )
  }
}


class App extends Component {
  render() {
    return (
      <Provider>
        <div>

          <Person/> {/* <---- add Person component */}

        </div>
      </Provider>
    );
  }
}


You can create nested elements to reference the Provider state. The following code demonstrates using a component named

class Age extends Component {

  render(){
    return (
      <Context.Consumer>

      {
        (context)=>(

             <p>{context.state.age}</p>
        )
      }

      </Context.Consumer>

    )
  }
}


class Person extends Component {
  render() {
    return (

        <Context.Consumer>
          {(context) => (                 
               
            <div>

              <p>Name: {context.state.name}</p> 
              
              <Age/>   {/*  <--- state*/}
              
            </div>

    
          )}
        </Context.Consumer>
 
    )
  }
}


Full Code

import React, { Component, createContext } from 'react';


const Context = React.createContext();


class Provider extends Component {
  state = {
    name: 'Bob',
    age: 33
  }
  render() {
    return (
      <Context.Provider value={{
        state: this.state
      }}>
        {this.props.children}
      </Context.Provider>
    )
  }
}


class Age extends Component {

  render(){
    return (
      <Context.Consumer>

      {
        (context)=>(

             <p>{context.state.age}</p>
        )
      }

      </Context.Consumer>

    )
  }
}

class Person extends Component {
  render() {
    return (

        <Context.Consumer>
          {(context) => (               
               
            <div>

              <p>Name: {context.state.name}</p> 
              
              <Age/>
            </div>

    
          )}
        </Context.Consumer>
 
    )
  }
}


class App extends Component {
  render() {
    return (
      <Provider>

        <Person/>

      </Provider>
    );
  }
}

export default App

Combining React useContext() Hook with Context API

You can combine a hook called useContext() with the Context API to make your code less confusing and easier to read. Hooks can only be used using function components. The code below has the same behavior as the previous code and the class components have been converted to functions.

import React, { Component, createContext } from 'react';


const Context = React.createContext();


function Provider(props){
  const state = {
    name: 'Bob',
    age: 33
  }

    return (
      <Context.Provider value={{
        state: state
      }}>
        {props.children}
      </Context.Provider>
    )
  
}


function Age(){

  
    return (
      <Context.Consumer>

      {
        (context)=>(

             <p>{context.state.age}</p>
        )
      }

      </Context.Consumer>

    )
  
}

function Person(){

    return (

        <Context.Consumer>
          {(context) => (               
               
            <div>

              <p>Name: {context.state.name}</p> 
              
              <Age/>
            </div>

    
          )}
        </Context.Consumer>
 
    )
  
}


function App(){

    return (
      <Provider>

        <Person/>

      </Provider>
    );
  
}

export default App

Reference the useContext Hook and Remove the Context.Consumer and Props Render Function.

Change the Person and Age functions to look like the following:



function Age() {

  const data = React.useContext(Context);
    return (


      <p>{data.state.age}</p>

    )

}

function Person() {

    const data = React.useContext(Context);

    return (

      <div>

        <p>Name: {data.state.name}</p> 
              
        <Age/>

      </div>



    )

}

Full Code

import React, { Component, createContext } from 'react';


const Context = React.createContext();


function Provider(props){
  const state = {
    name: 'Bob',
    age: 33
  }

    return (
      <Context.Provider value={{
        state: state
      }}>
        {props.children}
      </Context.Provider>
    )
  
}



function Age() {

  const data = React.useContext(Context);
    return (


      <p>{data.state.age}</p>

    )

}

function Person() {

    const data = React.useContext(Context);

    return (

      <div>

        <p>Name: {data.state.name}</p> 
              
        <Age/>

      </div>



    )

}


function App(){

    return (
      <Provider>

        <Person/>

      </Provider>
    );
  
}

export default App

Add Event Listeners to Change State

function Provider(props){
  const initialState = [
         {name:"Joe"},
         {name:"Bob"}
  ]
  const [state, updateState] = useState(initialState)

    return (
      <Context.Provider value={{
        state: state,
        changeName:function(){                                    
             updateState([...state,{name:"some new person"}])
        }
      }}>
        {props.children}
      </Context.Provider>
    )
  
}




function Person() {

    const data = React.useContext(Context);

    console.log(data)

    return (

      <div>

        <p onClick={data.changeName}>Name: {data.state[0].name}</p> 
              
             {data.state.map((val,index)=>{
                  

                 return <div key={index}>{val.name}</div>

             })}
      </div>



    )

}


function App(){

    return (
      <Provider>

        <Person/>

      </Provider>
    );
  
}

export default App

Same Code as Above with Object Literal as State

import React, { Component, createContext, useState} from 'react';

const Context = createContext();

const Provider = Context.Provider;


function ProviderContainer(props){
  const initialState = {                                                // <--- object literal as state (this is not an array!)
    name:"Bob",
    age:30
  }
  const [state, updateState] = useState(initialState)

    return (
      <Provider value={{
        state: state,
        changeName:function(){                                    
             updateState({...state, profession:"alien", namer:"oink"})   // <--- Object updated via a methos.
        }
      }}>
        {props.children}
      </Provider>
    )
  
}



function Person() {

    const data = React.useContext(Context);

    console.log(data)

    return (

      <div>

        <p onClick={data.changeName}>Name: {data.state.name}</p> 
              
             
      </div>



    )

}


function App(){

    return (
      <ProviderContainer>

        <Person/>

      </ProviderContainer>
    );
  
}

export default App

Todo List /Form Example


import React, {useEffect,createContext, useContext, useState, Component} from 'react';



const Context = createContext();



function ProviderContainer(props){
   
    	const initialState = {
			todo:"hi there"
		}


       const [state, setState] = useState(initialState);

       return (

       	    <Context.Provider value={{
       	    	state:state,
       	    	updateState:function(event,data){
                    event.preventDefault()
       	    		setState({todo:data})

       	    	}
       	    }}>

               
               {props.children}
       	    </Context.Provider>
       )
}



function TodoCreator(){

    const [formData, updateFormData] = useState("")
    const state = useContext(Context);

    console.log(formData)
	return (
      <div>
      <form onSubmit = {(event)=>{ 

      	state.updateState(event,formData) 

      	updateFormData("")


      }}>
         <input type="text" onChange = {(event)=>updateFormData(event.target.value)}/>
         <input type = "submit"/>
      </form>
      {state.state.todo}
      </div>
	)
}


function App(){


    
    return(
      <ProviderContainer>
          <TodoCreator/>
      </ProviderContainer>

    )

}






export default App

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment