How to update the Context value in Provider from the Consumer

后端 未结 5 861
日久生厌
日久生厌 2020-12-09 01:08

MyContext.js

import React from \"react\";

const MyContext = React.createContext(\'test\');
export default MyContext;

Cre

相关标签:
5条回答
  • 2020-12-09 01:47

    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

    0 讨论(0)
  • 2020-12-09 01:53

    You need to write a function in the Provider component to update the State. To be exact Consumer can only use the values and the function(s) you wrote in the Provider component.

    In Parent Component

    updateReturnMessage = (ReturnMessage) => {
      this.setState((prevState) => ({ ...prevState, ReturnMessage }))
    }
    
    <MyContext.Provider value={{ state: this.state, updateReturnMessage: this.updateReturnMessage }}>
    // your code goes here
    </MyContext.Provider>
    

    In Child Component:

    ClearData(e){
      const val = e.target.value;
      this.context.updateReturnMessage(val);
    }
    

    This function is similar to the action creators available in Redux and flux

    0 讨论(0)
  • 2020-12-09 02:03

    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);
    
    0 讨论(0)
  • 2020-12-09 02:05

    @nowshad, are you trying to use with redux Then I suggest using the provider

    import React from 'react'
    import { render } from 'react-dom'
    import { Provider } from 'react-redux'
    import { createStore } from 'redux'
    import todoApp from './reducers'
    import App from './components/App'
    ​
    const store = createStore(todoApp)
    ​
    render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    )
    

    If you are using for just few components and you want to have values for all nested components as per your statement

    For nested components can i have one provider and multiple consumers For an Example : 1 is an parent , 1.1 is a child to 1 and 1.1.1 is child to 1.1, Can i have provider to 1 and consumers to 1.1 and 1.1.1 
    

    then I suggest that you just pass a handler down as prop and once you want to change the state call the handler and it will change values throughout your components.(This should be done if you have just few child components, who all require the same values throughout)

    ***Using context, we can avoid passing props through intermediate elements***
    

    As per React Docs

    Don’t use context just to avoid passing props a few levels down. Stick to cases where the same data needs to be accessed in many components at multiple levels.

    Check Official Docs as to why and why not use Context: https://reactjs.org/docs/context.html

    Let me know if you still have issues or doubts as to why and how to use context

    0 讨论(0)
  • 2020-12-09 02:13

    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

    0 讨论(0)
提交回复
热议问题