React Context Api

The React context api is a simple messaging system similar in concept to the Publish/Subscribe pattern. It has three main parts you need to implement to use it: Context, Provider, and Consumer

React Context API Diagram

What about Redux?


Examples

Context

src/contexts/index.js

import React from "react";

export default ListContext = React.createContext()

Provider

src/containers/app.js

import React, { Component } from "react";
import { Provider } from "../contexts/";
import Input from "../components/input"; 
import List from "../components/list";


export default class extends Component{
    constructor (props) {
        super(props);
        
        // I used 'this.state' to be the object I passed to my provider,
        // but it can really be anything, including a single value 
        this.state = {
            itemList: [
                "Item 1: Context API, A New Hope",
                "Item 2: Redux Strikes Back",
                "Item 3: Return of Idempodency"
            ],
        }
    }
    
    // ...
    
    render () {
        return (
            <Provider value={this.state}>
                <div>
                    <h2>Todo List</h2>
                    <Input />
                    <List />
                </div>
            </Provider> 
        );
    }
}

Consumer

src/components/list.js

import React, { Component } from "react";
import { Consumer } from "../contexts/";

export default class extends Component {

    // ...

    render () {
        return (
            <Consumer>
                {(context) => (
                    <ul>
                        {context.items.map((title, index) => (
                            <li key={index}>{title}</li>
                        ))}
                    </ul>
                )}
            </Consumer>
        )
    }
}

How do I update the Context’s value from a Consumer?

The context api maintains React’s strict unidirectional data flow. If you need a Consumer to have the ability to update the Context’s value, you need to have a closure passed down from the Provider as part of it’s value (usually a property of the object that the Provider’s value is set to). Then you can access the closure from the value passed down from the Provider, and trigger whatever change you need as it still has the lexical scope of where it was created.

Example

Provider

src/containers/app.js

import React, { Component } from "react";
import { Provider } from "../contexts/";
import Input from "../components/input"; 
import List from "../components/list";


export default class extends Component{
    constructor (props) {
        super(props);
        this.state = {
            // The arrow function auto-binds 'this'. If you 
            // use a normal function/method, make sure to 
            // bind the appropriate context
            updateSelection: (name) => {
                this.setState({selectedName: name});
            },
            selectedName: ""
        }
    }
    
    // ...
    
    render () {
        return (
            <Provider value={this.state}>
                <div>
                    <h2>Todo List</h2>
                    <Input />
                    <List />
                </div>
            </Provider> 
        );
    }
}

Consumer

src/components/list.js

import React, { Component } from "react";
import { Consumer } from "../contexts/";

export default class extends Component {

    // ...

    render () {
        return (
            <Consumer>
                {(context) => (
                    <div>
                        { //  You can call it directly, or pass }
                        { //  it to a method you create to be   }
                        { //  used there                        }
                        <div onClick={context.updateSelection("Joe")}>
                            Select Joe!
                        </div>
                    </div>
                )}
            </Consumer>
        )
    }
}