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.

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

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}