In React, context is a way to pass data between component trees without manually adding props for each layer of components; context provides a way to share specified values between components without having to explicitly pass them through the component. Pass props layer by layer in the tree.

The operating environment of this tutorial: Windows 10 system, react version 17.0.1, Dell G3 computer.
Context provides a way to pass data between component trees without manually adding props to each layer of components. In a typical React application, data is passed from top to bottom (from parent to child) through props, but this approach is extremely cumbersome for certain types of properties (e.g. locale preferences, UI theme), these properties are required by many components in the application. Context provides a way to share such values between components without having to explicitly pass props through each level of the component tree.
Context is designed to share data that is "global" to a component tree, such as the currently authenticated user, theme, or preferred language. For example, in the following code, we manually adjust the style of a button component through a "theme" attribute
class App extends React.Component { render() { return <Toolbar theme="dark" />; }}function Toolbar(props) { // The Toolbar component accepts an additional "theme" property, which is then passed to the ThemedButton component. // It would be troublesome if every single button in the application needed to know the value of the theme, // because this value would have to be passed down to all components. return ( <p> <ThemedButton theme={props.theme} /> </p> );}class ThemedButton extends React.Component { render() { return <Button theme={this.props.theme} />; } }// Pass through props: App -> Toolbar -> ThemedButton// If the nesting is very deep, then props need to be passed layer by layer, even if the props are not needed in the middle, it seems very cumbersome.Using context, we can avoid passing props through intermediate elements
// Context allows us to pass values deep into the component tree without having to explicitly pass them through each component. // Create a context for the current theme ("light" is the default value). const ThemeContext = React.createContext('light');class App extends React.Component { render() { // Use a Provider to pass the current theme to the following component tree. // No matter how deep, any component can read this value. // In this example, we pass "dark" as the current value. return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); }}// The intermediate component no longer needs to specify the theme to be passed down. function Toolbar() { return ( <p> <ThemedButton /> </p> );}class ThemedButton extends React.Component { // Specify contextType to read the current theme context. // React will find the nearest theme provider and use its value. // In this example, the current theme value is "dark". static contextType = ThemeContext; render() { return <Button theme={this.context} />; }}// You can also use ThemedButto.contextType = ThemeContext;Create a Context object. When React renders a component subscribed to this Context object, the component will read the current context value from the matching Provider closest to itself in the component tree.
Only when there is no matching Provider in the tree where the component is located, its defaultValue parameter will take effect. This helps in testing components without using a Provider to wrap the component. Note: When undefined is passed to the Provider's value, the defaultValue of the consuming component will not take effect.
Each Context object returns a Provider React component, which allows consuming components to subscribe to context changes.
Provider receives a value attribute and passes it to the consuming component. A Provider can have a corresponding relationship with multiple consumer components. Multiple Providers can also be used nested, and the inner layer will overwrite the outer layer's data.
When the value of the Provider changes, all consuming components inside it will be re-rendered. Neither the Provider nor its internal consumer components are subject to the shouldComponentUpdate function, so the consumer component can be updated even if its ancestor component exits updating.
The contextType attribute mounted on the class will be reassigned to a Context object created by React.createContext(). This allows you to use this.context to consume the value on the most recent Context. You can access it in any life cycle, including the render function
import MyContext from './MyContext';class MyClass extends React.Component { componentDidMount() { let value = this.context; /* After the component is mounted, use the value of the MyContext component to perform some side-effect operations*/ } componentDidUpdate() { let value = this.context; /* ... */ } componentWillUnmount() { let value = this.context; /* ... */ } render() { let value = this.context; /* Render based on the value of the MyContext component*/ } // Or use static contextType = MyContext;}MyClass.contextType = MyContext; as in the above example.Here, React components can also subscribe to context changes. This allows you to subscribe to the context in functional components.
This requires function as a child. This function receives the current context value and returns a React node. The value passed to the function is equivalent to the value provided by the Provider closest to this context up the component tree. If there is no corresponding Provider, the value parameter is equivalent to the defaultValue passed to createContext().
The context object accepts a property named displayName, which is of type string. React DevTools uses this string to determine what context to display.
The following component will appear as MyDisplayName in DevTools
const MyContext = React.createContext(/* some value */);MyContext.displayName = 'MyDisplayName';<MyContext.Provider> // "MyDisplayName.Provider" in DevTools <MyContext.Consumer> // "MyDisplayName.Consumer" In DevToolsFor the above theme example, more complex usage can be achieved using dynamic values.
theme-context.js
export const themes = { light: { foreground: '#000000', background: '#eeeeee', }, dark: { foreground: '#ffffff', background: '#222222', },};export const ThemeContext = React .createContext(themes.dark); // This is the default valuethemed-button.js
import { ThemeContext } from './theme-context'; class ThemedButton extends React.Component { render() { let props = this.props; // Get the default value in ThemeContext let theme = this.context; return ( < button {...props} style={{backgroundColor: theme.background}} /> ); } // static contextType = ThemeContext;}ThemedButton.contextType = ThemeContext;export default ThemedButton;app.js
import { ThemeContext, themes } from './theme-context';import ThemedButton from './themed-button';// An intermediate component function using ThemedButton Toolbar(props) { return ( <ThemedButton onClick={props.changeTheme }> Change Theme </ThemedButton> );} class App extends React.Component { constructor(props) { super(props); this.state = { theme: themes.light, }; this.toggleTheme = () => { this.setState(state => ({ theme: state.theme === themes.dark ? themes.light : themes.dark, })); }; } render() { // Used by ThemedButton button component inside ThemeProvider The theme value in state, // and external components use the default theme value return ( <Page> <ThemeContext.Provider value={this.state.theme}> <Toolbar changeTheme={this.toggleTheme} /> </ThemeContext .Provider> <Section> <ThemedButton /> </Section> </Page> ); }}ReactDOM.render(<App />, document.root);// Components wrapped by ThemeContext.Provider can consume ThemeContext The value// in Toolbar and ThemedButton can use this.context to get the value// Note that the method of updating state is to pass it down through props, and the update is triggered by descendant components. The method through context will be discussed below. Pass update functionIn the above example, we pass an update function down through props to change the value of themes in the App. We know that it is necessary to update the context from a component that is deeply nested in the component tree. In this scenario, you can pass a function through the context to make the consumers component update the context.
theme-context.js
// Make sure the default value data structure passed to createContext is matched by the calling components (consumers)! export const ThemeContext = React.createContext({ theme: themes.dark, toggleTheme: () => {}, // Define the method to update the theme, pass it down});theme-toggler-button.js
import { ThemeContext } from './theme-context';function ThemeTogglerButton() { // The Theme Toggler button not only gets the theme value, it also gets a toggleTheme function from the context (app.js part below) return ( < ThemeContext.Consumer> {({theme, toggleTheme}) => ( <button onClick={toggleTheme} style={{backgroundColor: theme.background}}> Toggle Theme </button> )} </ThemeContext.Consumer> ); }export default ThemeTogglerButton;app.js
import { ThemeContext, themes } from './theme-context';import ThemeTogglerButton from './theme-toggler-button';class App extends React.Component { constructor(props) { super(props); this.toggleTheme = ( ) => { this.setState(state => ({ theme: state.theme === themes.dark ? themes.light : themes.dark, })); }; // State also contains the update function, so it Will be passed into the context provider. this.state = { theme: themes.light, toggleTheme: this.toggleTheme, // Define the update function and pass it down through the context }; } render() { // The entire state is passed into the provider return ( <ThemeContext. Provider value={this.state}> <Content /> </ThemeContext.Provider> ); }}function Content() { return ( <p> <ThemeTogglerButton /> </p> );}ReactDOM.render(<App />, document.root);In order to ensure that the context is re-rendered quickly, React needs to make the context of each consumer component a separate node in the component tree.
// Theme context, the default theme is "light" value const ThemeContext = React.createContext('light'); // User login context const UserContext = React.createContext({ name: 'Guest',}); class App extends React .Component { render() { const { signedInUser, theme } = this.props; // App component that provides the initial context value return ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser}> < Layout /> </UserContext.Provider> </ThemeContext.Provider> ); }}function Layout() { return ( <p> <Sidebar /> <Content /> </p> );}// A component may Consume multiple context functions Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext. Consumer> )} </ThemeContext.Consumer> );}If two or more context values are frequently used together, you may want to consider creating your own render component to provide these values.
Because the context uses a reference identity to decide when to render, there may be some pitfalls that may trigger unexpected rendering in the consumers component when the provider's parent component re-renders. For example, every time the Provider is re-rendered, the following code will re-render all the following consumer components, because the value attribute is always assigned to a new object
class App extends React.Component { render() { return ( <MyContext.Provider value={{something: 'something'}}> <Toolbar /> </MyContext.Provider> ); }}In order to prevent this situation, the value state is promoted to the state of the parent node.
class App extends React.Component { constructor(props) { super(props); // After multiple renderings, the state will be retained. When the value does not change, the following consumers components will not re-render this.state = { value: { something: 'something'}, }; } render() { return ( <Provider value={this.state.value}> <Toolbar /> </Provider> ); }}