ReactJS: setState pada induk di dalam komponen anak

91

Apa pola yang direkomendasikan untuk melakukan setState pada induk dari komponen anak.

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)

Saya memiliki larik item rencana yang dipertahankan dalam status induk. Saya ingin mengakses status induk dan menambahkan item rencana baru, dari komponen TodoForm's handleClick. Ide saya adalah melakukan setState pada induknya, yang akan membuat item rencana yang baru ditambahkan.

Pavithra
sumber
1
apakah ini membantu stackoverflow.com/questions/24147331/… ?
Dhiraj
Hanya akan mengirim
jujiyangasli
Saya mendapatkan kesalahansetState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the MyModal component.
Matt
Saya mendapatkan kesalahan yang sama bahwa saya tidak bisa setState pada komponen yang tidak terpasang. Apakah ada solusi untuk ini?
Kevin Burton

Jawaban:

82

Di orang tua Anda, Anda dapat membuat fungsi seperti addTodoItemyang akan melakukan setState yang diperlukan dan kemudian meneruskan fungsi itu sebagai props ke komponen anak.

var Todos = React.createClass({

  ...

  addTodoItem: function(todoItem) {
    this.setState(({ todos }) => ({ todos: { ...todos, todoItem } }));
  },

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

  ...

});

Anda dapat memanggil addTodoItemdi handleClick TodoForm. Ini akan melakukan setState pada induk yang akan membuat item rencana yang baru ditambahkan. Semoga Anda mendapatkan idenya.

Biola di sini.

Deepak
sumber
6
Apa yang dilakukan <<operator di this.state.todos << todoItem;sini?
Gabriel Garrett
@zavtra Sedikit kebingungan Ruby sedang terjadi
azium
7
Praktik buruk untuk bermutasi this.statesecara langsung. Lebih baik menggunakan setState fungsional. reactjs.org/docs/react-component.html#setstate
Rohmer
2
biola rusak
Hunter Nelson
1
Bagaimana solusi (yang diperbarui) ini diimplementasikan menggunakan React hooks?
ecoe
11

Ini semua pada dasarnya benar, saya hanya berpikir saya akan menunjuk ke dokumentasi reaksi resmi (ish) baru yang pada dasarnya merekomendasikan: -

Harus ada satu “sumber kebenaran” untuk setiap data yang berubah dalam aplikasi React. Biasanya, negara bagian pertama kali ditambahkan ke komponen yang membutuhkannya untuk rendering. Kemudian, jika komponen lain juga membutuhkannya, Anda dapat mengangkatnya ke leluhur terdekat yang terdekat. Alih-alih mencoba menyinkronkan status antara komponen yang berbeda, Anda harus mengandalkan aliran data dari atas ke bawah.

Lihat https://reactjs.org/docs/lifting-state-up.html . Halaman ini juga bekerja melalui sebuah contoh.

TattyFromMelbourne
sumber
8

Anda bisa membuat fungsi addTodo di komponen induk, mengikatnya ke konteks itu, meneruskannya ke komponen anak dan memanggilnya dari sana.

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

Kemudian, di Todos.render, Anda akan melakukannya

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

Sebut ini di TodoForm dengan

this.props.addToDo(newTodo);
reli
sumber
Ini sangat berguna. Tanpa melakukan bind(this)pada saat melewatkan fungsi, itu membuang kesalahan fungsi tersebut this.setState is not a function.
pratpor
7

Bagi mereka yang mempertahankan status dengan React Hook useState, saya menyesuaikan saran di atas untuk membuat Aplikasi slider demo di bawah ini. Dalam aplikasi demo, komponen penggeser anak mempertahankan status induknya.

Demo juga menggunakan useEffecthook. (dan yang kurang penting, useRefkail)

import React, { useState, useEffect, useCallback, 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 = useCallback(val => {
    setParentState(val);
  }, [setParentState]);

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

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

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

  const onSliderChangeHandler = e => {
  //pass slider's event value to child's 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;
NicoWheat
sumber
Anda dapat menggunakan aplikasi ini dengan create-react-app, dan mengganti semua kode di App.js dengan kode di atas.
NicoWheat
Hai, saya baru bereaksi dan bertanya-tanya: Apakah perlu digunakan useEffect? Mengapa kita perlu menyimpan data di status induk dan anak?
538ROMEO
1
Contoh tersebut tidak dimaksudkan untuk menunjukkan mengapa kita perlu menyimpan data baik pada orang tua maupun anak - sebagian besar waktu Anda tidak perlu melakukannya. Tetapi, jika Anda menemukan diri Anda dalam situasi di mana anak harus menetapkan keadaan orang tua, inilah cara Anda dapat melakukannya. useEffect diperlukan jika Anda ingin menyetel status induk SEBAGAI EFEK perubahan childState.
NicoWheat
3
parentSetState={(obj) => { this.setState(obj) }}
dezman
sumber
4
Meskipun kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang bagaimana dan / atau mengapa kode ini memecahkan masalah akan meningkatkan nilai jawaban jangka panjang.
Nic3500
2

Saya menemukan solusi yang berfungsi dan sederhana berikut untuk meneruskan argumen dari komponen anak ke komponen induk:

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

Lihat JSFIDDLE

Roma
sumber
0

Jika Anda bekerja dengan komponen kelas sebagai induk, salah satu cara paling sederhana untuk meneruskan setState ke anak adalah dengan meneruskannya di dalam fungsi panah. Ini berfungsi karena menetapkan lingkungan terangkat yang dapat diedarkan:

class ClassComponent ... {

    modifyState = () =>{
        this.setState({...})   
    }
    render(){
          return <><ChildComponent parentStateModifier={modifyState} /></>
    }
}
Julio Pereira
sumber