The original link www.robinwieruch.de/react-useco… & www.robinwieruch.de/react-conte…

React Context is a powerful feature. If your React application is bigger than a small application, give it a try. Many third-party libraries like Redux use it behind the scenes on the React Ecosystem.

If your component hierarchy grows vertically, it becomes redundant to pass multiple React components down — from parent components to deeply nested children. In most cases, any React component in the middle does not consume these props, but just passes the props to the next child component until it reaches the desired child component.

This tutorial shows you how to use the React Context for a simple use case.

REACT CONTEXT: How

Assuming the structure of the component tree is A -> B -> C, we need to provide downward data in component A.

First, you must create the React Context itself, which allows you to access the Provider and Consumer components. You can pass an initial value to createContext when creating a context. The initial value can also be null.

// src/ThemeContext.js
 
import React from 'react';
 
const ThemeContext = React.createContext(null);
 
export default ThemeContext;
Copy the code

Second, component A must provide context for the given Provider component. In this case, its value is immediately assigned to the component it needs, which can be anything from the component state (such as the data retrieved) to props. If the value comes from a modifiable React state, the value passed to the Provider component can also change.

// src/ComponentA.js
 
import React from 'react';
import ThemeContext from './ThemeContext';
 
const A = () => (
  <ThemeContext.Provider value="green">
    <B />
  </ThemeContext.Provider>
);
Copy the code

Component A only displays component B, although no items are passed to it, instead making the value green available to all of the React components below. One of the children will be component C, which ultimately uses the context.

Third, in component C, below component B, you can use context objects. Note that component A does not need to pass anything down through component B in props in order for it to reach component C.

// src/ComponentC.js
 
import React from 'react';
import ThemeContext from './ThemeContext';
 
const C = () => (
  <ThemeContext.Consumer>
    {value => (
      <p style={{ color: value }}>
        Hello World
      </p>
    )}
  </ThemeContext.Consumer>
);
Copy the code

Components can derive their styles by using context. The Consumer component makes the context passed available by using render items. As you can imagine, in this way, each component that needs to be styled according to the theme can now get the necessary information from the React Context by using the Consumer component of ThemeContext. Just use the Provider component, which passes values somewhere above them once.

REACT CONTEXT: WHEN

When should you use React Context? In general, there are two use cases when to use it:

  • When your React component hierarchy grows vertically in size, and you want to be able to pass props to child components without disturbing the components in between. We’ve used this use case as an example throughout the React Context tutorial.
  • When you want to use React Hooks in React for advanced state management to pass state and state updater functions through the React Context via the React application. Doing this with the React Context allows you to create shared and global states.

REACT’S USECONTEXT HOOK

Use useContext to rewrite the themecontext.consumer example above:

// src/ComponentC.js
 
import React from 'react';
import ThemeContext from './ThemeContext';
 
const C = () => {
    const value = React.useContext(ThemeContext);
    return (
      <p style={{ color: value }}>
        Hello World
      </p>
    )
};
Copy the code

The React useContext Hook retrieves the value from the Context as an argument. Using React Hook instead of the Consumer component makes the code more readable and concise, and does not introduce components in between (in this case, the Consumer component).

STATEFUL CONTEXT IN REACT WITH USECONTEXT

In the previous example, the context is a static (or stateless) value. But in most use cases, context will be used to pass stateful values. We will solve this problem now, because users may want to see variable theme colors.

const A = () => {
  const [color, setColor] = React.useState('green');
 
  return (
    <ThemeContext.Provider value={color}>
      <button type="button" onClick={() => setColor('red')}>
        Red Color
      </button>
      <button type="button" onClick={() => setColor('yellow')}>
        Yellow Color
      </button>
 
      <B />
      <D />
    </ThemeContext.Provider>
  );
};
Copy the code

The inline event handler changes the stateful value by clicking one of the buttons. Because rerendering occurs after a state change, the modified value is passed through the Provider component to all child components, which display it as a dynamic value. One thing to think about here is that the component D is also re-rendered, which is not the result we want, because D doesn’t use this value at all.

BEST PRACTICES FOR CONTEXT AND USECONTEXT

There are some best practices to follow when using React Context and useContext. So far, you’ve covered the basics. This section goes beyond these basics by showing you how to use context in a larger React project and attempts to address one of the remaining issues of repeated rendering above.

When I create a new file for React Context, I always start with the basics (as described earlier) :

// src/ThemeContext.js
import React from 'react';
 
const ThemeContext = React.createContext(null);
 
export { ThemeContext };
Copy the code

The first thing I would like to improve is to provide a custom context hook to access the context:

// src/ThemeContext.js
import React from 'react';
 
const ThemeContext = React.createContext(null);
 
const useColor = () => React.useContext(ThemeContext);
 
export { ThemeContext, useColor };
Copy the code

I then use this new custom context hook instead of using useContext as a mediator:

// src/ComponentC.js
 
import React from 'react';
import { ThemeContext, useColor } from './ThemeContext';
 
const C = () => {
    const value = useColor();
    return (
      <p style={{ color: value }}>
        Hello World
      </p>
    )
};
Copy the code

Or, if I have to use the context in a third party (such as Styled Components), I will expose a HOC:

// src/ThemeContext.js import React from 'react'; const ThemeContext = React.createContext(null); const useColor = () => React.useContext(ThemeContext); const withColor = (Component) => (props) => { const value = useColor(); return <Component {... props} value={value} />; }; // if ref is used // // const withColor = (Component) => // React.forwardRef((props, ref) => { // const value = useColor(); // return <Component {... props} ref={ref} value={value} />; / /}); export { ThemeContext, useColor, withColor };Copy the code

Third, like custom context hooks, you can also provide custom Provider components:

// src/ThemeContext.js
import React from 'react';
 
const ThemeContext = React.createContext(null);
 
const useColor = () => React.useContext(ThemeContext);
 
const ColorProvider = ({ value, children }) => {
  return (
    <ThemeContext.Provider value='green'>
      {children}
    </ThemeContext.Provider>
  );
};
 
export { ColorProvider, useColor };
Copy the code

Note that the ThemeContext itself is no longer exported. Instead, it is the new custom Provider component that is used in the A component and still receives stateful values:

// src/ComponentA.js
 
import React from 'react';
import { ColorProvider, useColor } from './ThemeContext';
 
const A = () => {
    const [color, setColor] = React.useState('green');
    return (
      <ColorProvider value={color}>
        <B />
      </ColorProvider>
    )
};
Copy the code

Now that the ThemeContext is completely wrapped up, we just need to add the color dictionary and state. The complete code looks like this:

import React from 'react';
const ThemeContext = React.createContext(null);
const COLORS = {
  GREEN: 'green',
  RED: 'red',
  YELLOW: 'yellow'
};
const ColorProvider = ({ children }) => {
  const [color, setColor] = React.useState(COLORS.GREEN);
  return (
    <CurrencyContext.Provider value={[color, setColor]}>
      {children}
    </CurrencyContext.Provider>
  );
};
const useColor = () => {
  const [color, setColor] = React.useContext(ThemeContext);
  const handleColor = (value) => {
    setColor(value);
  };
  return { value: color, onChange: handleColor };
};
export { ColorProvider, useColor, COLORS };
Copy the code

That’s it! We encapsulated the state and state update logic in our custom Provider component and custom context hooks. Anyone using the new API can access the state and a function to update it throughout the component tree in their React application. Used in A component:

import React from 'react';
import B from './B';
import { ColorProvider, useColor, COLORS } from './ThemeContext';
 
export default A = () => (
  <ColorProvider>
    <B />
  </ColorProvider>
);
Copy the code

Used in C components:

import React from 'react';
import { ColorProvider, useColor, COLORS } from './ThemeContext';
 
export default D = () => {
	const { value, onChange } = useColor()
	return (
		<>
		<button type="button" onClick={() => onChange(COLORS.RED)}>
			Red Color
		</button>
		<button type="button" onClick={() => onChange(COLORS.YELLOW)}>
			Yellow Color
		</button>
        <p style={{ color: value }}>
        	Hello World
        </p>
		</>
	)
}
Copy the code