How to update React Context from inside a child component?

前端 未结 3 1940
醉梦人生
醉梦人生 2020-11-30 19:10

I have the language settings in the context as like below

class LanguageProvider extends Component {
  static childContextTypes = {
    langConfig: PropType         


        
相关标签:
3条回答
  • 2020-11-30 19:22

    The Above answer by Maithani is a great solution but that is a class component without the use of hooks. Since it is recommended by React to use functional components and hooks so I will implement it with useContext and useState hooks. Here is how you can update the context from within a child component.

    LanguageContextMangement.js

    import React, { useState } from 'react'
    
    export const LanguageContext = React.createContext({
      language: "en",
      setLanguage: () => {}
    })
    
    export const LanguageContextProvider = (props) => {
    
      const setLanguage = (language) => {
        setState({...state, language: language})
      }
    
      const initState = {
        language: "en",
        setLanguage: setLanguage
      } 
    
      const [state, setState] = useState(initState)
    
      return (
        <LanguageContext.Provider value={state}>
          {props.children}
        </LanguageContext.Provider>
      )
    }
    

    App.js

    import React, { useContext } from 'react'
    import { LanguageContextProvider, LanguageContext } from './LanguageContextManagement'
    
    function App() {
    
      const state = useContext(LanguageContext)
    
      return (
        <LanguageContextProvider>
          <button onClick={() => state.setLanguage('pk')}>
            Current Language is: {state.language}
          </button>
        </LanguageContextProvider>
      )
    }
    
    export default App
    
    0 讨论(0)
  • 2020-11-30 19:27

    Using hooks

    Hooks were introduced in 16.8.0 so the following code requires a minimum version of 16.8.0 (scroll down for the class components example). CodeSandbox Demo

    1. Setting parent state for dynamic context

    Firstly, in order to have a dynamic context which can be passed to the consumers, I'll use the parent's state. This ensures that I've a single source of truth going forth. For example, my parent App will look like this:

    const App = () => {
      const [language, setLanguage] = useState("en");
      const value = { language, setLanguage };
    
      return (
        ...
      );
    };
    

    The language is stored in the state. We will pass both language and the setter function setLanguage via context later.

    2. Creating a context

    Next, I created a language context like this:

    // set the defaults
    const LanguageContext = React.createContext({
      language: "en",
      setLanguage: () => {}
    });
    

    Here I'm setting the defaults for language ('en') and a setLanguage function which will be sent by the context provider to the consumer(s). These are only defaults and I'll provide their values when using the provider component in the parent App.

    Note: the LanguageContext remains same whether you

    3. Creating a context consumer

    In order to have the language switcher set the language, it should have the access to the language setter function via context. It can look something like this:

    const LanguageSwitcher = () => {
      const { language, setLanguage } = useContext(LanguageContext);
      return (
        <button onClick={() => setLanguage("jp")}>
          Switch Language (Current: {language})
        </button>
      );
    };
    

    Here I'm just setting the language to 'jp' but you may have your own logic to set languages for this.

    4. Wrapping the consumer in a provider

    Now I'll render my language switcher component in a LanguageContext.Provider and pass in the values which have to be sent via context to any level deeper. Here's how my parent App look like:

    const App = () => {
      const [language, setLanguage] = useState("en");
      const value = { language, setLanguage };
    
      return (
        <LanguageContext.Provider value={value}>
          <h2>Current Language: {language}</h2>
          <p>Click button to change to jp</p>
          <div>
            {/* Can be nested */}
            <LanguageSwitcher />
          </div>
        </LanguageContext.Provider>
      );
    };
    

    Now, whenever the language switcher is clicked it updates the context dynamically.

    CodeSandbox Demo

    Using class components

    The latest context API was introduced in React 16.3 which provides a great way of having a dynamic context. The following code requires a minimum version of 16.3.0. CodeSandbox Demo

    1. Setting parent state for dynamic context

    Firstly, in order to have a dynamic context which can be passed to the consumers, I'll use the parent's state. This ensures that I've a single source of truth going forth. For example, my parent App will look like this:

    class App extends Component {
      setLanguage = language => {
        this.setState({ language });
      };
    
      state = {
        language: "en",
        setLanguage: this.setLanguage
      };
    
      ...
    }
    

    The language is stored in the state along with a language setter method, which you may keep outside the state tree.

    2. Creating a context

    Next, I created a language context like this:

    // set the defaults
    const LanguageContext = React.createContext({
      language: "en",
      setLanguage: () => {}
    });
    

    Here I'm setting the defaults for language ('en') and a setLanguage function which will be sent by the context provider to the consumer(s). These are only defaults and I'll provide their values when using the provider component in the parent App.

    3. Creating a context consumer

    In order to have the language switcher set the language, it should have the access to the language setter function via context. It can look something like this:

    class LanguageSwitcher extends Component {
      render() {
        return (
          <LanguageContext.Consumer>
            {({ language, setLanguage }) => (
              <button onClick={() => setLanguage("jp")}>
                Switch Language (Current: {language})
              </button>
            )}
          </LanguageContext.Consumer>
        );
      }
    }
    

    Here I'm just setting the language to 'jp' but you may have your own logic to set languages for this.

    4. Wrapping the consumer in a provider

    Now I'll render my language switcher component in a LanguageContext.Provider and pass in the values which have to be sent via context to any level deeper. Here's how my parent App look like:

    class App extends Component {
      setLanguage = language => {
        this.setState({ language });
      };
    
      state = {
        language: "en",
        setLanguage: this.setLanguage
      };
    
      render() {
        return (
          <LanguageContext.Provider value={this.state}>
            <h2>Current Language: {this.state.language}</h2>
            <p>Click button to change to jp</p>
            <div>
              {/* Can be nested */}
              <LanguageSwitcher />
            </div>
          </LanguageContext.Provider>
        );
      }
    }
    

    Now, whenever the language switcher is clicked it updates the context dynamically.

    CodeSandbox Demo

    0 讨论(0)
  • 2020-11-30 19:40

    One quite simple solution is to set state on your context by including a setState method in your provider like so:

    return ( 
                <Context.Provider value={{
                  state: this.state,
                  updateLanguage: (returnVal) => {
                    this.setState({
                      language: returnVal
                    })
                  }
                }}> 
                  {this.props.children} 
                </Context.Provider>
            )
    

    And in your consumer, call updateLanguage like so:

    // button that sets language config
    <Context.Consumer>
    {(context) => 
      <button onClick={context.updateLanguage({language})}> 
        Set to {language} // if you have a dynamic val for language
      </button>
    <Context.Consumer>
    
    0 讨论(0)
提交回复
热议问题