Подтвердить что ты не робот

ReactJS: setState на родительском внутреннем дочернем компоненте

Каков рекомендуемый шаблон для создания setState для родителя из дочернего компонента.

var Todos = React.createClass({
  getInitialState: function() {
    return {
      todos: [
        "I am done",
        "I am not done"
      ]
    }
  },

  render: function() {
    var todos = this.state.todos.map(function(todo) {
      return <div>{todo}</div>;
    });

    return <div>
      <h3>Todo(s)</h3>
      {todos}
      <TodoForm />
    </div>;
  }
});

var TodoForm = React.createClass({
  getInitialState: function() {
    return {
      todoInput: ""
    }
  },

  handleOnChange: function(e) {
    e.preventDefault();
    this.setState({todoInput: e.target.value});
  },

  handleClick: function(e) {
    e.preventDefault();
    //add the new todo item
  },

  render: function() {
    return <div>
      <br />
      <input type="text" value={this.state.todoInput} onChange={this.handleOnChange} />
      <button onClick={this.handleClick}>Add Todo</button>
    </div>;
  }
});

React.render(<Todos />, document.body)

У меня есть массив todo-элементов, который поддерживается в родительском состоянии. Я хочу получить доступ к родительскому состоянию и добавить новый элемент todo из компонента TodoForm handleClick. Моя идея состоит в том, чтобы сделать setState для родителя, который будет отображать новый добавленный элемент todo.

4b9b3361

Ответ 1

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

var Todos = React.createClass({

  ...

  addTodoItem: function(todoItem) {
    this.state.todos.push(todoItem);
    this.setState({todos: this.state.todos});
  },

  render: function() {

    ...

    return <div>
      <h3>Todo(s)</h3>
      {todos}
      <TodoForm addTodoItem={this.addTodoItem} />
    </div>
  }
});

var TodoForm = React.createClass({
  handleClick: function(e) {
    e.preventDefault();
    this.props.addTodoItem(this.state.todoInput);
    this.setState({todoInput: ""});
  },

  ...

});

Вы можете вызвать addTodoItem в методе TodoForm handleClick. Это будет делать setState для родителя, который будет отображать новый добавленный элемент todo. Надеюсь, вы поняли эту идею.

Заклинание здесь.

Ответ 2

Все они в основном правильные, я просто подумал, что укажу на новую (ish) официальную документацию по реагированию, которая в основном рекомендует: -

Должен быть единый "источник правды" для любых данных, которые изменяются в приложении React. Обычно состояние сначала добавляется к компоненту, который нуждается в нем для рендеринга. Затем, если другие компоненты также нуждаются в этом, вы можете поднять его до своего ближайшего общего предка. Вместо того, чтобы пытаться синхронизировать состояние между различными компонентами, вы должны полагаться на нисходящий поток данных.

Смотрите https://reactjs.org/docs/lifting-state-up.html. Страница также работает через пример.

Ответ 3

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

// in Todos
addTodo: function(newTodo) {
    // add todo
}

Затем, в Todos.render, вы сделали бы

<TodoForm addToDo={this.addTodo.bind(this)} />

Позвоните в TodoForm с помощью

this.props.addToDo(newTodo);

Ответ 4

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

//ChildExt component
class ChildExt extends React.Component {
    render() {
        var handleForUpdate =   this.props.handleForUpdate;
        return (<div><button onClick={() => handleForUpdate('someNewVar')}>Push me</button></div>
        )
    }
}

//Parent component
class ParentExt extends React.Component {   
    constructor(props) {
        super(props);
        var handleForUpdate = this.handleForUpdate.bind(this);
    }
    handleForUpdate(someArg){
            alert('We pass argument from Child to Parent: \n' + someArg);   
    }

    render() {
        var handleForUpdate =   this.handleForUpdate;    
        return (<div>
                    <ChildExt handleForUpdate = {handleForUpdate.bind(this)} /></div>)
    }
}

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

Посмотрите на JSFIDDLE

Ответ 5

parentSetState={(obj) => { this.setState(obj) }}

Ответ 6

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

Демо также использует хук useEffect. (и, что менее важно, useRef хук)

import React, { useState, useEffect, useRef } from "react";

//the parent react component
function Parent() {

  // the parentState will be set by its child slider component
  const [parentState, setParentState] = useState(0);

  // make wrapper function to give child
  const wrapperSetParentState = val => {
    setParentState(val);
  };

  return (
    <div style={{ margin: 30 }}>
      <Child
        parentState={parentState}
        parentStateSetter={wrapperSetParentState}
      />
      <div>Parent State: {parentState}</div>
    </div>
  );
};

//the child react component
function Child(props) {
  const childRef = useRef();
  const [childState, setChildState] = useState(0);

  useEffect(() => {
    props.parentStateSetter(childState);
  }, [childState]);

  const onSliderChangeHandler = e => {
  //pass slider event value to child state
    setChildState(e.target.value);
  };

  return (
    <div>
      <input
        type="range"
        min="1"
        max="255"
        value={childState}
        ref={childRef}
        onChange={onSliderChangeHandler}
      ></input>
    </div>
  );
};

export default Parent;