如何从子组件内部更新React Context?

时间:2016-12-08 01:35:06

标签: javascript reactjs

我在上下文中有语言设置,如下所示

class LanguageProvider extends Component {
  static childContextTypes = {
    langConfig: PropTypes.object,
  };

  getChildContext() {
    return { langConfig: 'en' };
  }

  render() {
    return this.props.children;
  }
}

export default LanguageProvider;

我的应用程序代码如下所示

<LanguageProvider>
  <App>
    <MyPage />
  </App>
</LanguageProvider>

我的页面有一个用于切换语言的组件

<MyPage>
  <LanguageSwitcher/>
</MyPage>
LanguageSwitcher中的

MyPage需要更新上下文,才能将语言更改为&#39; jp&#39;如下

class LanguageSwitcher extends Component {
  static contextTypes = {
    langConfig: PropTypes.object,
  };

  updateLanguage() {
    //Here I need to update the langConfig to 'jp' 
  }

  render() {
    return <button onClick={this.updateLanguage}>Change Language</button>;
  }
}

export default LanguageSwitcher;

如何从LanguageSwitcher组件内部更新上下文?

4 个答案:

答案 0 :(得分:36)

注意:我使用的是最新的context API,到目前为止,我使用的是React 16.3,它提供了一种具有动态上下文的好方法。 CodeSandbox Demo

1。为动态上下文设置父状态

首先,为了拥有可以传递给使用者的动态上下文,我将使用父级的状态。这样可以确保我拥有单一的真理来源。例如,我的父应用程序将如下所示:

class App extends Component {
  setLanguage = language => {
    this.setState({ language });
  };

  state = {
    language: "en",
    setLanguage: this.setLanguage
  };

  ...
}

language与语言设置器方法一起存储在状态中,您可以将其保留在状态树之外。

2。创建上下文

接下来,我创建了一个这样的语言上下文:

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

在这里,我为language('en')和setLanguage函数设置了默认值,这些默认值将由上下文提供者发送给使用者。这些只是默认值,在父级App中使用提供者组件时,我将提供它们的值。

3。创建上下文使用者

为了让语言切换器设置语言,它应该可以通过上下文访问语言设置器功能。它可能看起来像这样:

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

在这里,我只是将语言设置为'jp',但是您可能有自己的逻辑来设置语言。

4。将消费者包装在提供商中

现在,我将在LanguageContext.Provider中呈现我的语言切换器组件,并将必须通过上下文发送的值传递到更深的任何级别。这是我的父母App的样子:

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>
    );
  }
}

现在,每当单击语言切换器时,它都会动态更新上下文。

CodeSandbox Demo

答案 1 :(得分:28)

Maithani的上述答案是一个不错的解决方案,但这是不使用钩子的类组件。由于React建议使用功能组件和挂钩,因此我将使用useContext和useState挂钩实现它。这是从子组件中更新上下文的方法。

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

答案 2 :(得分:3)

一个非常简单的解决方案是通过在提供程序中包含setState方法来设置上下文状态,如下所示:

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

在您的消费者中,这样调用updateLanguage:

// 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>

答案 3 :(得分:0)

我个人喜欢这种模式:

文件:context.jsx

DateFormat

然后你可以创建一个功能组件,如果它是提供者树中的一个孩子:

文件:component.jsx

    def treeview_sort_column(tv, col, reverse):
        l = [(tv.set(k, col), k) for k in tv.get_children('')]
        l.sort(key=lambda t: t[0], reverse=reverse)
        #      ^^^^^^^^^^^^^^^^^^^^^^^
    
        for index, (val, k) in enumerate(l):
            tv.move(k, '', index)
    
        tv.heading(col,
                   command=lambda: treeview_sort_column(tv, col, not reverse))
    
    root = Tk()
    columns = ('name', 'number',)
    treeview = ttk.Treeview(root, columns=columns, show='headings')
    for t in (('one', '1'), ('ten', '10'), ('eleven', '11'), ('two', '2'), ('three', '3')):
        treeview.insert('', END, values=(t[0],t[1]))
    treeview.pack()
    for col in columns:
        treeview.heading(col, text=col,
                         command=lambda c=col: treeview_sort_column(treeview, c, False))
    
    mainloop()