How to update the Context value in a Provider from the Consumer? How to update the Context value in a Provider from the Consumer? reactjs reactjs

How to update the Context value in a Provider from the Consumer?


You could use the useContext hook to achieve this. It's quite easy to use it in the child elements of the Provider. As an example...

authContext.js

import { createContext } from "react";const authContext = createContext({  authenticated: false,  setAuthenticated: (auth) => {}});export default authContext;

Login.js (component consuming the Context)

import React, { useContext } from "react";import authContext from "./authContext";export default () => {  const { setAuthenticated } = useContext(authContext);  const handleLogin = () => setAuthenticated(true);  const handleLogout = () => setAuthenticated(false);  return (    <React.Fragment>      <button onClick={handleLogin}>login</button>      <button onClick={handleLogout}>logout</button>    </React.Fragment>  );};

Finally the index.js

import ReactDOM from "react-dom";import React, { useState } from "react";import authContext from "./authContext";import Login from "./Login";const App = () => {  const [authenticated, setAuthenticated] = useState(false);  return (    <authContext.Provider value={{ authenticated, setAuthenticated }}>      <div> user is {`${authenticated ? "" : "not"} authenticated`} </div>      <Login />    </authContext.Provider>  );};ReactDOM.render(<App />, document.getElementById("container"));

As you can see, it becomes quite easy to consume the data stored in the context using the useContext hook. Of course, as with every React hook, it only works with functional components.

If you want to see the code working.https://codesandbox.io/s/react-playground-forked-wbqsh?file=/index.js


Updating Context from a Nested Component

It is often necessary to update the context from a component that is nested somewhere deeply in the component tree. In this case you can pass a function down through the context to allow consumers to update the context:

theme-context.js

// Make sure the shape of the default value passed to// createContext matches the shape that the consumers expect!export const ThemeContext = React.createContext({  theme: themes.dark,  toggleTheme: () => {},});

theme-toggler-button.js

import {ThemeContext} from './theme-context';function ThemeTogglerButton() {  // The Theme Toggler Button receives not only the theme  // but also a toggleTheme function from the context  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 updater function so it will    // be passed down into the context provider    this.state = {      theme: themes.light,      toggleTheme: this.toggleTheme,    };  }  render() {    // The entire state is passed to the provider    return (      <ThemeContext.Provider value={this.state}>        <Content />      </ThemeContext.Provider>    );  }}function Content() {  return (    <div>      <ThemeTogglerButton />    </div>  );}ReactDOM.render(<App />, document.root);

The above example is straight from the React Context API docs v16.8.6, and is the recommended way to update a context value from a consumer. https://reactjs.org/docs/context.html#updating-context-from-a-nested-component


Firstly, in order to update the context from the consumer, you need to access the context outside of the render function, For details on how to do this, check

Access React Context outside of render function

Secondly, you should provide a handler from Provider which updates the context value and not mutate it directly. Your code will look like

Parent.js

import MyContext from "./MyContext.js";import Child from "./Child.js";class Parent extends Component {    constructor(props) {      super(props);      this.state = {        Message: "Welcome React",        ReturnMessage:""      };    }    updateValue = (key, val) => {       this.setState({[key]: val});    }    render() {        return (           <MyContext.Provider value={{state: this.state, updateValue: this.updateValue}}>                    <Child />            </MyContext.Provider>       )    }}

Child

import MyContext from "./MyContext.js";class Child extends Component {    constructor(props) {      super(props);      this.state = {                ReturnMessage:""      };    }    ClearData(e){        const val = e.target.value;        this.setState({           ReturnMessage:val        });        this.props.context.updateValue('ReturnMessage', val);    }    render() {        return (           <React.Fragment>             <p>{this.props.context.state.Message}</p>}             <input onChange={this.ClearData} />           </React.Fragment>       )    }}const withContext = (Component) => {   return (props) => {       <MyContext.Consumer>                {(context) => {               return <Component {...props} context={context} />            }}       </MyContext.Consumer>   }}export default withContext(Child);