Using Context API, contextType and useContext()
Context
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
In a typical React application, data is passed top-down (parent to child) via props, but this can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.
Using context, we can avoid passing props through intermediate elements.
React.createContext
const MyContext = React.createContext(defaultValue);
Creates a Context object. When React renders a component that subscribes to this Context object it will read the current context value from the closest matching Provider above it in the tree.
The defaultValue argument is only used when a component does not have a matching Provider above it in the tree. This can be helpful for testing components in isolation without wrapping them. Note: passing undefined as a Provider value does not cause consuming components to use defaultValue.
Context.Provider
<MyContext.Provider value={/* some value */}>
Every Context object comes with a Provider React component that allows consuming components to subscribe to context changes.
Accepts a value prop to be passed to consuming components that are descendants of this Provider. One Provider can be connected to many consumers. Providers can be nested to override values deeper within the tree.
All consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant consumers is not subject to the shouldComponentUpdate method, so the consumer is updated even when an ancestor component bails out of the update.
Class.contextType
class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* perform a side-effect at mount using the value of MyContext */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* render something based on the value of MyContext */
}
}
MyClass.contextType = MyContext;
The contextType property on a class can be assigned a Context object created by React.createContext(). This lets you consume the nearest current value of that Context type using this.context. You can reference this in any of the lifecycle methods including the render function.
Note:
You can only subscribe to a single context using this API. If you need to read more than one see Consuming Multiple Contexts.
If you are using the experimental public class fields syntax, you can use a static class field to initialize your contextType.
class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
/* render something based on the value */
}
}
Context.Consumer
<MyContext.Consumer>
{value => /* render something based on the context value */}
</MyContext.Consumer>
A React component that subscribes to context changes. This lets you subscribe to a context within a function component.
Requires a function as a child. The function receives the current context value and returns a React node. The value argument passed to the function will be equal to the value prop of the closest Provider for this context above in the tree. If there is no Provider for this context above, the value argument will be equal to the defaultValue that was passed to createContext().
Example:
auth-context.js:
import React from 'react';
const authContext = React.createContext({
authenticated: false,
login: () => {}
});
export default authContext;
App.js:
import AuthContext from '../context/auth-context';
<AuthContext.Provider
value={{
authenticated: this.state.authenticated,
login: this.loginHandler
}}
>
{this.state.showCockpit ? (
<Cockpit
title={this.props.appTitle}
showPersons={this.state.showPersons}
personsLength={this.state.persons.length}
clicked={this.togglePersonsHandler}
/>
) : null}
{persons}
</AuthContext.Provider>
Cockpit.js (Functional Component) :
import React, { useContext } from 'react';
import AuthContext from '../../context/auth-context';
const cockpit = props => {
const authContext = useContext(AuthContext); // We can use useContext in Functional Components just like we use the static class variable in Class Components
console.log(authContext.authenticated);
return (
<div className={classes.Cockpit}>
<h1>{props.title}</h1>
<p className={assignedClasses.join(' ')}>This is really working!</p>
<button ref={toggleBtnRef} className={btnClass} onClick={props.clicked}>
Toggle Persons
</button>
<button onClick={authContext.login}>Log in</button>
</div>
);
}
Person.js:
import AuthContext from '../../../context/auth-context';
class Person extends Component {
static contextType = AuthContext; // This type of creating a static variable is not supported in Functional Components
componentDidMount() {
console.log(this.context.authenticated);
}
render() {
console.log('[Person.js] rendering...');
return (
<Aux>
{this.context.authenticated ? (
<p>Authenticated!</p>
) : (
<p>Please log in</p>
)}
</Aux>
);
}
}
Note: We can also use Context.Consumer in both Functional and Class Components but the way that was shown above is much more compact and reusable.
Comments
Post a Comment