简体   繁体   English

如何从消费者更新提供者中的上下文值?

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

MyContext.js我的上下文.js

import React from "react";

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

I created my context in a separated js file where I can access my parent as well as my child component我在一个单独的js文件中创建了我的上下文,我可以在其中访问我的父组件和我的子组件

Parent.js父.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>
       )
    }
}

So I created the parent component with a Provider context and calling child component in the provider tab所以我用 Provider 上下文创建了父组件,并在 provider 选项卡中调用子组件

Child.js儿童.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>
       )
    }
}

So in child by using the Consumer , I can display the data in child rendering part.所以在 child 通过使用Consumer ,我可以在 child 渲染部分显示数据。

I'm facing an issue when I want to update the state from the consumer.当我想从消费者那里更新状态时,我遇到了一个问题。

How to update provider state or manipulate state of provider?如何更新提供者状态或操作提供者的状态?

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主题上下文.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主题切换器-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应用程序.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.上面的示例直接来自 React Context API docs v16.8.6,并且是从使用者更新上下文值的推荐方法。 https://reactjs.org/docs/context.html#updating-context-from-a-nested-component 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 在渲染函数之外访问 React Context

Secondly, you should provide a handler from Provider which updates the context value and not mutate it directly.其次,您应该从 Provider 提供一个处理程序,它更新上下文值而不是直接改变它。 Your code will look like你的代码看起来像

Parent.js父.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);

You could use the useContext hook to achieve this.您可以使用 useContext 钩子来实现这一点。 It's quite easy to use it in the child elements of the Provider.在 Provider 的子元素中使用它非常容易。 As an example...举个例子...

authContext.js authContext.js

import { createContext } from "react";

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

export default authContext;

Login.js (component consuming the Context) 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>
  );
};

Finally the index.js最后是 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.如您所见,使用 useContext 钩子使用存储在上下文中的数据变得非常容易。 Of course, as with every React hook, it only works with functional components.当然,就像每个 React 钩子一样,它只适用于函数式组件。

If you want to see the code working.如果你想看到代码工作。 https://codesandbox.io/s/react-playground-forked-wbqsh?file=/index.js https://codesandbox.io/s/react-playground-forked-wbqsh?file=/index.js

You need to write a function in the Provider component to update the State.您需要在 Provider 组件中编写一个函数来更新 State。 To be exact Consumer can only use the values and the function(s) you wrote in the Provider component.确切地说,消费者只能使用您在 Provider 组件中编写的值和函数。

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此功能类似于Reduxflux可用的action creators

@nowshad, are you trying to use with redux Then I suggest using the provider @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')
)

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)那么我建议您只将处理程序作为 prop 向下传递,一旦您想更改状态,调用处理程序,它将更改整个组件的值。(如果您只有几个子组件,它们都需要相同的内容,则应该这样做贯穿始终的价值观)

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

As per React Docs根据 React 文档

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检查官方文档以了解为什么以及为什么不使用 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如果您对为什么以及如何使用上下文仍有问题或疑问,请告诉我

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM