Как обновить родительское состояние в React?

Моя структура выглядит следующим образом:

Component 1  

 - |- Component 2


 - - |- Component 4


 - - -  |- Component 5  

Component 3

Компонент 3 должен отображать некоторые данные в зависимости от состояния компонента 5. Поскольку реквизит неизменен, я не могу просто сохранить его в компоненте 1 и переслать его, правильно? И да, я читал о редуксе, но не хочу его использовать. Надеюсь, что это можно решить, просто отреагируя. Я не прав?

Ответ 1

Для общения между родителями и детьми вы должны передать функцию, устанавливающую состояние от родителя к ребенку, например:

class Parent extends React.Component {
  constructor(props) {
    super(props)

    this.handler = this.handler.bind(this)
  }

  handler(someValue) {
    this.setState({
      someVar: someValue
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {this.props.handler}/ >
  }
}

Ответ 2

Я нашел следующее рабочее решение для передачи аргумента функции onClick от дочернего к родительскому компоненту:

Версия с передачей метода()

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component {

    constructor(props) {
        super(props);
        var handleToUpdate  = this.handleToUpdate.bind(this);
        var arg1 = '';
    }

    handleToUpdate(someArg){
            alert('We pass argument from Child to Parent: ' + someArg);
            this.setState({arg1:someArg});
    }

    render() {
        var handleToUpdate  =   this.handleToUpdate;

        return (<div>
                    <ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <ParentA />,
        document.querySelector("#demo")
    );
}

Посмотри на JSFIDDLE

Версия с передачей функции Arrow

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div>
          <button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component { 
    constructor(props) {
        super(props);
    }

    handleToUpdate = (someArg) => {
            alert('We pass argument from Child to Parent: ' + someArg);
    }

    render() {
        return (<div>
            <ChildB handleToUpdate = {this.handleToUpdate} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <ParentA />,
        document.querySelector("#demo")
    );
}

Посмотри на JSFIDDLE

Ответ 3

Мне нравится ответ о передаче функций вокруг, его очень удобный метод.

С обратной стороны вы также можете добиться этого, используя pub/sub или используя вариант, диспетчер, как Flux. Теория супер проста, компонент 5 отправляет сообщение, которое прослушивает компонент 3. Компонент 3 затем обновляет свое состояние, которое запускает повторную визуализацию. Для этого требуются компоненты с состоянием, которые, в зависимости от вашей точки зрения, могут быть или не быть анти-шаблоном. Я против них лично и предпочел бы, чтобы что-то еще слушало рассылки и изменения из состояния сверху вниз (Redux делает это, но добавляет дополнительную терминологию).

import { Dispatcher } from flux
import { Component } from React

const dispatcher = new Dispatcher()

// Component 3
// Some methods, such as constructor, omitted for brevity
class StatefulParent extends Component {
  state = {
    text: 'foo'
  } 

  componentDidMount() {
    dispatcher.register( dispatch => {
      if ( dispatch.type === 'change' ) {
        this.setState({ text: 'bar' })
      }
    }
  }

  render() {
    return <h1>{ this.state.text }</h1>
  }
}

// Click handler
const onClick = event => {
  dispatcher.dispatch({
    type: 'change'
  })
}

// Component 5 in your example
const StatelessChild = props => {
  return <button onClick={ onClick }>Click me</button> 
}

Диспетчерские пакеты с Flux очень просты, он просто регистрирует обратные вызовы и вызывает их при любой отправке, проходящей через содержимое в диспетчере (в приведенном выше примере нет отправки payload с отправкой, просто сообщение Я бы). Вы можете легко адаптировать его к традиционному паб/суб (например, с помощью EventEmitter из событий или какой-либо другой версии), если это имеет для вас больше смысла.

Ответ 4

Я хочу поблагодарить наиболее одобренный ответ за то, что он дал мне представление о моей собственной проблеме, в основном, о ее изменении с помощью функции стрелки и передачи параметра из дочернего компонента:

 class Parent extends React.Component {
  constructor(props) {
    super(props)
    // without bind, replaced by arrow func below
  }

  handler = (val) => {
    this.setState({
      someVar: val
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {() => this.props.handler('the passing value')}/ >
  }
}

Надеюсь, это поможет кому-то.

Ответ 5

Я нашел следующее рабочее решение для передачи аргумента функции onClick из дочернего элемента в родительский компонент с параметром:

родительский класс:

class Parent extends React.Component {
constructor(props) {
    super(props)

    // Bind the this context to the handler function
    this.handler = this.handler.bind(this);

    // Set some state
    this.state = {
        messageShown: false
    };
}

// This method will be sent to the child component
handler(param1) {
console.log(param1);
    this.setState({
        messageShown: true
    });
}

// Render the child component and set the action property with the handler as value
render() {
    return <Child action={this.handler} />
}}

дочерний класс:

class Child extends React.Component {
render() {
    return (
        <div>
            {/* The button will execute the handler function set by the parent component */}
            <Button onClick={this.props.action.bind(this,param1)} />
        </div>
    )
} }

Ответ 6

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

В родительском компоненте в вашем случае компонент 3

static childContextTypes = {
        parentMethod: React.PropTypes.func.isRequired
      };

       getChildContext() {
        return {
          parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child)
        };
      }

parentMethod(parameter_from_child){
// update the state with parameter_from_child
}

Теперь в дочернем компоненте (компонент 5 в вашем случае), просто скажите этому компоненту, что он хочет использовать контекст своего родителя.

 static contextTypes = {
       parentMethod: React.PropTypes.func.isRequired
     };
render(){
    return(
      <TouchableHighlight
        onPress={() =>this.context.parentMethod(new_state_value)}
         underlayColor='gray' >   

            <Text> update state in parent component </Text>              

      </TouchableHighlight>
)}

Вы можете найти демо-проект в репо

Ответ 7

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

Мы передаем функцию, определенную в родительском элементе, дочернему элементу как "props" и вызываем эту функцию из дочернего элемента, вызывая ее в родительском компоненте.


class Parent extends React.Component {
  handler = (Value_Passed_From_SubChild) => {
    console.log("Parent got triggered when a grandchild button was clicked");
    console.log("Parent->Child->SubChild");
    console.log(Value_Passed_From_SubChild);
  }
  render() {
    return <Child handler = {this.handler} />
  }
}
class Child extends React.Component {
  render() {
    return <SubChild handler = {this.props.handler}/ >
  }
}
class SubChild extends React.Component { 
  constructor(props){
   super(props);
   this.state = {
      somethingImp : [1,2,3,4]
   }
  }
  render() {
     return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/>
  }
}
React.render(<Parent />,document.getElementById('app'));

 HTML
 ----
 <div id="app"></div>

В этом примере мы можем передать данные из SubChild → Child → Parent, передав функцию ее непосредственному Child.

Ответ 8

-We может создать ParentComponent и с помощью метода handleInputChange для обновления состояния ParentComponent. Импортируйте ChildComponent, и мы передаем два реквизита от родительского к дочернему компоненту, т.е. функции и count.handleInputChange.

import React, { Component } from 'react';
import ChildComponent from './ChildComponent';

class ParentComponent extends Component {
  constructor(props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.state = {
      count: '',
    };
  }

  handleInputChange(e) {
    const { value, name } = e.target;
    this.setState({ [name]: value });
  }

  render() {
    const { count } = this.state;
    return (
      <ChildComponent count={count} handleInputChange={this.handleInputChange} />
    );
  }
}
  • Теперь мы создаем файл ChildComponent и сохраняем как ChildComponent.jsx. Этот компонент не имеет состояния, потому что дочерний компонент не имеет состояния. Мы используем библиотеку prop-types для проверки типов реквизита.

    import React from 'react';
    import { func, number } from 'prop-types';
    
    const ChildComponent = ({ handleInputChange, count }) => (
      <input onChange={handleInputChange} value={count} name="count" />
    );
    
    ChildComponent.propTypes = {
      count: number,
      handleInputChange: func.isRequired,
    };
    
    ChildComponent.defaultProps = {
      count: 0,
    };
    
    export default ChildComponent;
    

Ответ 9

Я использовал ответ с этой страницей с самым высоким рейтингом много раз, но, изучая React, я нашел лучший способ сделать это, без привязки и без встроенной функции в подпорках.

Просто посмотрите здесь:

class Parent extends React.Component {

  constructor() {
    super();
    this.state={
      someVar: value
    }
  }

  handleChange=(someValue)=>{
    this.setState({someVar: someValue})
  }

  render() {
    return <Child handler={this.handleChange} />
  }

}

export const Child = ({handler}) => {
  return <Button onClick={handler} />
}

Ключ находится в функции стрелки:

handleChange=(someValue)=>{
  this.setState({someVar: someValue})
}

Вы можете прочитать больше здесь. Надеюсь это кому-нибудь пригодится =)

Ответ 10

поэтому, если вы хотите обновить родительский компонент,

 class ParentComponent extends React.Component {
        constructor(props){
            super(props);
            this.state = {
               page:0
            }
        }

        handler(val){
            console.log(val) // 1
        }

        render(){
          return (
              <ChildComponent onChange={this.handler} />
           )
       }
   }


class ChildComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
             page:1
        };
    }

    someMethod = (page) => {
        this.setState({ page: page });
        this.props.onChange(page)
    }

    render() {
        return (
       <Button
            onClick={() => this.someMethod()} 
       > Click
        </Button>
      )
   }
}

Здесь onChange - это атрибут с привязанным к нему экземпляром метода-обработчика. мы передали обработчик метода компоненту класса Child, чтобы получить через свойство onChange его аргумент props.

Атрибут onChange будет установлен в объекте props следующим образом:

props ={
onChange : this.handler
}

и передается дочернему компоненту

Таким образом, дочерний компонент может получить доступ к значению имени в объекте props, например, props.onChange

Это сделано с помощью реквизита рендеринга.

Теперь у дочернего компонента есть кнопка "Click" с событием onclick, установленным для вызова метода-обработчика, переданного ему через onChnge в его объекте аргумента props. Так что теперь this.props.onChange в Child содержит метод вывода в классе Parent. Ссылка и кредиты: Битыи кусочки

Ответ 11

Если этот сценарий не распространяется повсеместно, вы можете использовать контекст React, особенно если вы не хотите вводить все накладные расходы, которые представляют библиотеки управления состоянием. Плюс легче учиться. Но будьте осторожны, вы можете злоупотребить этим и начать писать плохой код. По сути, вы определяете компонент Контейнер (который будет хранить и сохранять этот фрагмент состояния для вас), делая все компоненты, заинтересованные в записи/чтении этого фрагмента данных, его дочерними (не обязательно прямыми дочерними)

https://reactjs.org/docs/context.html

  Вместо этого вы также можете использовать обычный React.

<Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} />

передать doSomethingAbout5 до компонента 1

    <Component1>
        <Component2 onSomethingHappenedIn5={somethingAbout5 => this.setState({somethingAbout5})}/>
        <Component5 propThatDependsOn5={this.state.somethingAbout5}/>
    <Component1/>

Если это обычная проблема, вы должны начать думать о переносе всего состояния приложения в другое место. У вас есть несколько вариантов, наиболее распространенными из которых являются:

https://redux.js.org/

https://facebook.github.io/flux/

  По сути, вместо управления состоянием приложения в вашем компоненте вы отправляете команды, когда что-то происходит для обновления состояния. Компоненты также извлекают состояние из этого контейнера, поэтому все данные централизованы. Это не значит, что я больше не могу использовать местное государство, но это более сложная тема.

Ответ 12

<Footer 
  action={()=>this.setState({showChart: true})}
/>

<footer className="row">
    <button type="button" onClick={this.props.action}>Edit</button>
  {console.log(this.props)}
</footer>

Try this example to write inline setState, it avoids creating another function.