Как обновить значение контекста в поставщике от потребителя

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

Таким образом, создан родительский компонент с контекстом поставщика и вызовом дочернего компонента на вкладке поставщика

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

Таким образом, у ребенка с помощью потребителя может отображаться часть дочернего рендеринга.

Я столкнулся с обновлением от потребителя до состояния поставщика.

Как обновить состояние поставщика или управлять состоянием поставщика.

Ответы

Ответ 1

Во-первых, чтобы обновить контекст от потребителя, вам нужно получить доступ к контексту вне функции рендеринга. Подробнее о том, как это сделать, проверьте

Контекст доступа к контексту вне функции рендеринга

Во-вторых, вы должны предоставить обработчик от поставщика, который обновляет значение контекста и не изменяет его напрямую. Ваш код будет выглядеть так:

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

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

Ответ 2

Вам нужно написать функцию в компоненте 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);
}

Эта функция похожа на action creators доступных в Redux и flux

Ответ 3

Обновление контекста из вложенного компонента

Часто необходимо обновить контекст компонента, который находится где-то глубоко в дереве компонентов. В этом случае вы можете пропустить функцию через контекст, чтобы потребители могли обновить контекст:

тема-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: () => {},
});

тема-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 v.8.8.6 и является рекомендуемым способом обновления значения контекста от потребителя. https://reactjs.org/docs/context.html#updating-context-from-a-nested-component

Ответ 4

@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***

Согласно Реагирующим Документам

Не используйте контекст, чтобы избежать передачи реквизита на несколько уровней. Придерживайтесь случаев, когда одни и те же данные должны быть доступны во многих компонентах на разных уровнях.

Проверьте официальные документы о том, почему и почему не использовать контекст: https://reactjs.org/docs/context.html

Дайте мне знать, если у вас все еще есть проблемы или сомнения относительно того, почему и как использовать контекст