如何从Consumer更新Provider中的Context值

时间:2018-05-24 06:44:19

标签: javascript reactjs react-context

MyContext.js

import React from "react";

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

创建了一个上下文单独的js文件,我可以在父文件和子组件中访问该文件

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:""
      };
    }

    render() {
        return (
           <MyContext.Provider value={{state: this.state}}>      
              <Child /> 
           </MyContext.Provider>
       )
    }
}

因此创建了一个父组件,使用Provider上下文并在提供者选项卡中调用子组件

Child.js

import MyContext from "./MyContext.js";

class Child extends Component {

    constructor(props) {
      super(props);
      this.state = {        
        ReturnMessage:""
      };
    }

    ClearData(context){
        this.setState({
           ReturnMessage:e.target.value
        });
        context.state.ReturnMessage = ReturnMessage
    }

    render() {
        return (
           <MyContext.Consumer>                 
              {(context) => <p>{context.state.Message}</p>}
              <input onChange={this.ClearData(context)} />
           </MyContext.Consumer>
       )
    }
}

因此在使用消费者的孩子中可以显示儿童渲染部分..

我正面临从消费者到提供者状态的更新。

如何更新提供者状态或操纵提供者状态..

5 个答案:

答案 0 :(得分:5)

您可以使用useContext挂钩来实现此目的。在Provider的子元素中使用它非常容易。例如...

authContext.js

import { createContext } from "react";

const authContext = createContext({
  authenticated: false,
  setAuthenticated: (auth) => {}
});

export default authContext;

Login.js(使用上下文的组件)

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

最后是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"));

如您所见,使用useContext挂钩可以很容易地使用存储在上下文中的数据。当然,与每个React钩子一样,它仅适用于功能组件。

如果您想查看代码是否正常工作。 https://codesandbox.io/s/react-playground-forked-wbqsh?file=/index.js

答案 1 :(得分:4)

首先,为了从使用者更新上下文,您需要访问render函数之外的上下文。有关如何执行此操作的详细信息,请检查

Access React Context outside of render function

其次,您应该从Provider提供一个处理程序,它更新上下文值而不是直接改变它。您的代码看起来像

<强> Parent.js

import(tidyverse)

ex %>%
  group_by(lat, long) %>%
  summarise(closest_clim = which.min(abs(lat - clim$lat) + 
                                       abs(long - clim$long))) %>%
  mutate(alt = clim$alt[closest_clim],
         x = clim$x[closest_clim],
         y = clim$y[closest_clim])

# A tibble: 3 x 6
# Groups:   lat [3]
    lat  long closest_clim    alt     x     y
  <dbl> <dbl>        <int>  <dbl> <dbl> <dbl>
1   40.   10.            4 0.0333    0. 1921.
2   55.    6.            1 0.0333    0. 1914.
3   60.    6.           10 0.0333    0. 1884.

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

答案 2 :(得分:1)

从嵌套组件更新上下文

通常有必要从嵌套在组件树中某个位置的组件更新上下文。在这种情况下,您可以在上下文中向下传递一个函数,以允许使用者更新上下文:

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

上面的示例直接来自React Context API文档v16.8.6,是从使用者更新上下文值的推荐方法。 https://reactjs.org/docs/context.html#updating-context-from-a-nested-component

答案 3 :(得分:0)

您需要在Provider组件中编写一个函数以更新State。 确切地说,消费者只能使用您在Provider组件中编写的值和函数。

在父组件中

articles

在子组件中:

this.state.newsPaper.map(async (querypara) => {

    const requstone = await fetch(`https:someapisources=${querypara}&apiKey=${api_key}`);
    const dataone = await requstone.json();

    // Concatenate articles from response (dataone) to array in state
    this.setState({
        articles: this.state.articles.concat(dataone.articles)
    });
}

此功能类似于updateReturnMessage = (ReturnMessage) => { this.setState((prevState) => ({ ...prevState, ReturnMessage })) } <MyContext.Provider value={{ state: this.state, updateReturnMessage: this.updateReturnMessage }}> // your code goes here </MyContext.Provider> ClearData(e){ const val = e.target.value; this.context.updateReturnMessage(val); } 中可用的action creators

答案 4 :(得分:-3)

@nowshad,您是否正在尝试使用redux 然后我建议使用提供者

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')
)

如果您只使用少数几个组件,并且希望根据您的语句获得所有嵌套组件的值

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 

然后我建议您只需将处理程序作为prop传递,一旦您想要更改状态调用处理程序,它将在整个组件中更改值。(如果您只有少数子组件,那么这应该完成在整个过程中需要相同的值

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

根据React Docs

  

不要仅使用上下文来避免将道具向下移动几级。棒   在许多组件中需要访问相同数据的情况   在多个层面。

检查官方文档,了解为何以及为何不使用Context: https://reactjs.org/docs/context.html

如果您对使用上下文的原因和方法仍有疑问或疑问,请与我们联系