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

MyContext.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

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

So I created the parent component with a Provider context and calling child component in the provider tab

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

So in child by using the Consumer, I can display the data in child rendering part.

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?

163317 次浏览

首先,为了从使用者那里更新上下文,你需要访问渲染函数之外的上下文

在呈现函数之外访问反应上下文

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

家长

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

孩子

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

@ 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

然后我建议你只需要传递一个处理程序作为道具,一旦你想要改变状态调用处理程序,它就会改变整个组件的值。(如果只有几个子组件,而且所有子组件都需要相同的值,则应该这样做)

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

根据反应文件

不要仅仅为了避免传递道具而使用上下文 对于需要在许多组件中访问相同数据的情况 at multiple levels.

查阅官方文件,了解为什么和为什么不使用上下文: Https://reactjs.org/docs/context.html

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

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

在父组件中

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

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

从嵌套组件更新上下文

通常需要从嵌套在组件树深处的组件更新上下文。在这种情况下,您可以通过上下文向下传递一个函数,以允许使用者更新上下文:

主题-上下文

// 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: () => {},
});

主题-切换器-按钮

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;

应用程序

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

您可以使用 useContext 挂钩来实现这一点。在 Provider 的子元素中使用它非常容易。举个例子。

AuthContext.js

import { createContext } from "react";


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


export default authContext;

Js (使用 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>
);
};

最后是 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