[React Context] The use of Context in React

I. Overview

Context provides a way to share values ​​between components without having to explicitly pass props through the layers of the component tree. If the levels of obtaining the value and using the value are far apart, or there are many components that need to use this value and are scattered, you can use Context to share data and avoid using a large number of repeated props to pass values. If only one component needs to use this value, you can generate this component at the location where this value is generated, and then use props to pass it layer by layer to the actual display location of the component.

2. Basic usage

1. Custom Context

import React from 'react';
 
const ThemeContext = React.createContext('light');
 
export default ThemeContext;

The code above defines a ThemeContext with a default value of 'light'.

2. Use the Provider of Context where needed

import ThemeContext from './context/ThemeContext.js';
import ThemedButton from './ThemedButton.js';
import './App.css';
 
function App() {
    
    
  return (
    <ThemeContext.Provider value='dark'>
      <div className="App">
        <header className="App-header">
          <ThemedButton />
        </header>
      </div>
    </ThemeContext.Provider>
  );
}
 
export default App;

The provider of the custom Context is used at the outermost layer of the component, and the incoming value overrides the default value, and then the value of ThemeContext read by the subcomponent is 'dark' instead of the default value 'light'. If the Provider has a value definition, the value of the value will be used (even if the value is undefined, that is, no value is passed in), and only when the Provider does not provide it, the default value at the time of definition will be used.

3. Define the contextType and use the value obtained from the Context

import React, {
    
     Component } from 'react';
import ThemeContext from "./context/ThemeContext.js";
 
class ThemedButton extends Component {
    
    
	static contextType = ThemeContext;
	render() {
    
    
		return <button>{
    
    this.context}</button>;
	}
}
 
export default ThemedButton;

ThemedButton declares that the contextType is ThemeContext, so the value of this.context is the value provided by the nearest ThemeContext, which is 'light'.

Renderings:
insert image description here

4. The name of Context
The Context declared in the above way will only display Context.Porvider when viewing the component tree, and will not display ThemeContext:

insert image description here

This is inconvenient to view when there are multiple Providers. To define the display name of Context, you can declare it with displayName when defining:

import React from 'react';
 
const ThemeContext = React.createContext('light');
ThemeContext.displayName = 'ThemeContext';
 
export default ThemeContext;

In this way, the component tree will display the name of the Context.

insert image description here

3. Context update mechanism

If the value attribute of the Provider is a variable, when the value changes, the subcomponent that consumes the Provider will trigger the update mechanism, thereby also implementing the update. Whether the value changes is judged by the method of Object.is, so do not use dynamically generated objects as the value of context, such as:

<ThemeContext.Provider value={
    
    {
    
     theme: 'light' }}><ThemeContext.Provider />

Since the value pointed to by value is a newly generated object every time it is rendered, the memory pointer is always different, which causes the update of the component that consumes this Provider to be triggered every time. So use a variable to carry this value.

1. The life cycle
App.js triggered by Context update

import React, {
    
     Component } from 'react';
import ThemeContext from './context/ThemeContext.js';
import ThemedButton from './ThemedButton.js';
import './App.css';
 
class App extends Component {
    
    
 
  constructor(props) {
    
    
    super(props);
    this.state = {
    
    
      theme: 'dark'
    };
  }
 
  switchTheme = () => {
    
    
    let {
    
     theme } = this.state;
    theme = theme === 'dark' ? 'light' : 'dark';
    this.setState({
    
     theme });
  }
 
  render() {
    
    
    return (
      <ThemeContext.Provider value={
    
    this.state.theme}>
        <div className="App">
          <header className="App-header">
            <ThemedButton onClick={
    
    this.switchTheme} />
          </header>
        </div>
      </ThemeContext.Provider>
    );
  }
}
 
export default App;

ThemedButton.js

import React, {
    
     Component } from 'react';
import ThemeContext from "./context/ThemeContext.js";
 
class ThemedButton extends Component {
    
    
	static contextType = ThemeContext;
 
	componentDidUpdate() {
    
    
		console.log('ThemedButton componentDidUpdate');
	}
 
	shouldComponentUpdate() {
    
    
		console.log('ThemedButton shouldComponentUpdate');
		return false;
	}
 
	render() {
    
    
		console.log('ThemedButton render');
		return <button onClick={
    
    this.props.onClick}>{
    
    this.context}</button>;
	}
}
 
export default ThemedButton;

Output on update:
insert image description here

The update of Context is not affected by the parent component and its own shouldComponentUpdate, and the life cycle is render->componentDidUpdate.

2. Both props update and Context update trigger
the life cycle triggered by props update is shouldComponentUpdate->render->componentDidUpdate. If Context update occurs, the overlapping life cycle of the two will be triggered, that is, render->componentDidUpdate. Therefore, if there is a need to trigger the shouldComponentUpdate life cycle, it should be avoided to be triggered at the same time as the Context update.

4. Use Consumer to support obtaining values ​​on multiple Contexts

1. Basic method of use
If you need to use values ​​on multiple Contexts, only one contextType is not enough. You need to use the Consumer provided by Context to obtain the values ​​on the corresponding Context.

Declare two Context types:

import React from 'react';
 
const ThemeContext = React.createContext('light');
ThemeContext.displayName = 'ThemeContext';
 
export default ThemeContext;
import React from 'react';
 
const UserContext = React.createContext('guest');
UserContext.displayName = 'UserContext';
 
export default UserContext;

Use Provider assignment:

import React, {
    
     Component } from 'react';
import ThemeContext from './context/ThemeContext.js';
import UserContext from './context/UserContext.js';
import ThemedButton from './ThemedButton.js';
import './App.css';
 
class App extends Component {
    
    
 
  constructor(props) {
    
    
    super(props);
  }
 
  render() {
    
    
    return (
        <ThemeContext.Provider value={
    
    'dark'}>
          <div className="App">
            <UserContext.Provider value={
    
    'user'}>
              <header className="App-header">
                <ThemedButton />
              </header>
            </UserContext.Provider>
          </div>
        </ThemeContext.Provider>
    );
  }
}
 
export default App;

Use the values ​​provided by these two Contexts in ThemedButton:

import React, {
    
     Component } from 'react';
import ThemeContext from "./context/ThemeContext.js";
import UserContext from "./context/UserContext.js";
 
class ThemedButton extends Component {
    
    
 
	render() {
    
    
		return (
			<>
				<ThemeContext.Consumer>
					{
    
    theme => <div>{
    
    theme}</div>}
				</ThemeContext.Consumer>
				<UserContext.Consumer>
					{
    
    user => <div>{
    
    user}</div>}
				</UserContext.Consumer>
			</>
		);
	}
}
 
export default ThemedButton;

Within the scope of the Consumer package, you can use the value provided by the corresponding Provider. In this way, you can consume the values ​​provided by multiple types of Providers in a component.

2. Comparison between contextType and Consumer
The contextType can only point to one Context in a component, and then the value provided by the corresponding Provider can be used anywhere in the component through this.context. The disadvantage is that only one Context can be used. Consumer can use multiple to get the value of different types of Providers. However, due to the syntax of tags, it can only be used in render and related scopes. Components should use Consumer as much as possible to obtain the value on Context, and leave the position of contextType to avoid that there is no contextType to use when contextType must be used later.

Five, custom Context

In the above Context definition, only Context and the corresponding display name are defined, and Context.Provider and Context.Consumer are used for subsequent use. Such a label language is difficult to understand. In addition, when assigning a value to Provider, the value attribute is used, which does not reflect the exact meaning, especially when there are multiple values ​​​​to be passed in. Therefore, there should be the following parts when customizing Context:

import React from 'react';
import PropTypes from 'prop-types';
 
// 枚举值
const THEMES = {
    
    
	DARK: 'dark',
	LIGHT: 'light'
};
// 默认值
const defaultValue = THEMES.DARK;
// Context
const ThemeContext = React.createContext(defaultValue);
// Context展示名
ThemeContext.displayName = 'ThemeContext';
// Consumer
const ThemeConsumer = ThemeContext.Consumer;
// Provider
const ThemeProvider = ({
    
     theme, children }) => (
	<ThemeContext.Provider value={
    
    theme}>
		{
    
    children}
	</ThemeContext.Provider>
);
 
ThemeProvider.propTypes = {
    
    
 
	theme: PropTypes.oneOf(['dark', 'light']),
 
	children: PropTypes.oneOfType([
		PropTypes.element,
		PropTypes.arrayOf(PropTypes.element)
	]),
}
 
export {
    
     THEMES, ThemeContext, ThemeConsumer, ThemeProvider };

when using it:

import React, {
    
     Component } from 'react';
import {
    
     ThemeProvider, THEMES } from './context/ThemeContext.js';
import ThemedButton from './ThemedButton.js';
import './App.css';
 
class App extends Component {
    
    
 
  render() {
    
    
    // 不再需要ThemeContext.Provider和value,用更易理解的标签取代
    return (
      <ThemeProvider theme={
    
    THEMES.LIGHT}>
        <div className="App">
          <header className="App-header">
            <ThemedButton />
          </header>
        </div>
      </ThemeProvider>
    );
  }
}
 
export default App;
import React, {
    
     Component } from 'react';
import {
    
     ThemeConsumer } from "./context/ThemeContext.js";
 
class ThemedButton extends Component {
    
    
 
  render() {
    
    
    // 用ThemeConsumer取代ThemeContext.Consumer
    // 也可以用contextType = ThemeContext,ThemeContext也有导出
    // 但是如之前所说,不到万不得已不使用只有一个位置的contextType
    return (
      <ThemeConsumer>
        {
    
    theme => <div>{
    
    theme}</div>}
      </ThemeConsumer>
    );
  }
}
 
export default ThemedButton;

Six, the use of useContext

React introduced Hook in version 16.8, which allows you to use state and other React features without writing a class. useContext is one of the native Hooks. It is a function component that can also use Context, and supports the use of multiple different types of Context. So if you use function components, you can use useContext to support the use of multiple different types of Context values.

Provide the value of Context is still the same code:

import React, {
    
     Component } from 'react';
import {
    
     ThemeProvider, THEMES } from './context/ThemeContext.js';
import UserContext from './context/UserContext';
import ThemeAndUser from './ThemeAndUser';
import './App.css';
 
class App extends Component {
    
    
 
  render() {
    
    
    return (
      <ThemeProvider theme={
    
    THEMES.LIGHT}>
        <UserContext.Provider value={
    
    'user'}>
          <div className="App">
            <header className="App-header">
              <ThemeAndUser />
            </header>
          </div>
        </UserContext.Provider>
      </ThemeProvider>
    );
  }
}
 
export default App;

Use useContext to get the value on Context:

import {
    
     useContext } from 'react';
import {
    
     ThemeContext } from './context/ThemeContext';
import UserContext from './context/UserContext';
 
const ThemeAndUser = () => {
    
    
	const theme = useContext(ThemeContext);
	const user = useContext(UserContext);
	return (
		<>
			<div>{
    
    theme}</div>
			<div>{
    
    user}</div>
		</>
	);
};
 
export default ThemeAndUser;

7. Summary

Context is used to provide globally accessible content with a controllable update mechanism. But try not to use it unless necessary.
Context can not only pass values, but also functions. You can pass the value in the context together with the function to update the value.
contextType can only point to one Context, and use one Context value. Using Context.Consumer can use multiple Context values, but only in render and its related functions.
useContext can also use multiple Context values, but it can only be used in function components.

8. References

1. React's official Chinese documentation on using Context: https://zh-hans.reactjs.org/docs/context.html

2. React's official Chinese documentation on using useContext: https://zh-hans.reactjs.org/docs/hooks-reference.html#usecontext

————————————————
Copyright statement: This article is an original article of CSDN blogger "sysukehan", following the CC 4.0 BY-SA copyright agreement, please attach the original source link and this statement for reprinting .
Original link: https://blog.csdn.net/sysukehan/article/details/114039009

Guess you like

Origin blog.csdn.net/hzxOnlineOk/article/details/130011954