in classic React, to make one component's state accessible to a sibling, we need to move state up to the parent and then pass it down via props to both children. so as more components need a certain piece of information from state, we have to lift state further and further up, and pass props further and further down. this is called prop drilling.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3b0e0662-fd76-456b-b8ab-eab98f3c008a/Untitled.png

React Context allows you to bypass this by passing data directly from an ancestor to a descendant, using the Provider and Consumer wrappers:

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2b9ad16f-a056-45a9-b93a-647afa4f9580/Untitled.png

producing context:

import React from "react"
import ReactDOM from "react-dom"

import App from "./App"
const { Consumer, Provider } = React.createContext()

const AppWithContext = () => (
	<Provider value={"light"}>
		{/* now, any component in this subtree can access the context. */}
		<App />
	</Provider>
)
ReactDOM.render(<AppWithContext />, document.getElementById("root"))

consuming context (class components)

for class components, the simplest way to consume context is to set the contextType static property. so either MyComponent.contextType = ExampleContext outside the class, or static contextType = ThemeContext inside the class:

import React from "react"
import ThemeContext from "./themeContext"

class Button extends Component {
    render() {
        const theme = this.context
        return (
            <button className={`${theme}-theme`}>Switch Theme</button>
        )    
    }
}

Button.contextType = ThemeContext

where themeContext.js just creates a context to be shared across components:

import React from "react"
const ThemeContext = React.createContext()
export default ThemeContext

consuming context (functional components)

for functional components, you can access context by rendering the Consumer component created in React.createContext(), and passing it a render prop that will have access to context. the render prop must be passed to Consumer's children prop:

import React from "react"
import ThemeContext from "./themeContext"

function Button(props) {
    return (
        <ThemeContext.Consumer>
            {theme => (
                <button className={`${theme}-theme`}>Switch Theme</button>
            )}
        </ThemeContext.Consumer>
				{/* remember, the above is equivalent to the following:
						<ThemeContext.Consumer children={theme => ...} />
						(see the [section on React children](<https://www.notion.so/Reusability-407214c480bd4047ab5a4ec8d98200e5#ed3670c7c4e24ac097081732a81f8f94>))
				*/}
    )    
}

export default Button

you don't have to wrap your entire component in the Consumer, you can just wrap the part that actually needs context:

function App() {
    return (
        <div>
            <main>
                <UserContext.Consumer>
                    {username => (
                        <p className="main">Hello, {username}! 🎉</p>
                    )}
                </UserContext.Consumer>
            </main>
        </div>
    )
}

in a Consuming component, you can do whatever you want with context, including passing it down as props to a child.

changing context

of course, you can pass an object to the Provider instead of just a primitive value; the object can include a toggle function to update the context value:

import React, {Component} from "react"
const {Provider, Consumer} = React.createContext()

class ThemeContextProvider extends Component {
    state = {
        theme: "dark"
    }
    
    toggleTheme = () => {
        this.setState(prevState => {
            return {
                theme: prevState.theme === "light" ? "dark" : "light"
            }
        })
    }
    
    render() {
        return (
            <Provider value={{theme: this.state.theme, toggleTheme: this.toggleTheme}}>
                {this.props.children}
            </Provider>
        )
    }
}

export {ThemeContextProvider, Consumer as ThemeContextConsumer}