ReactJS: setState trên thành phần cha bên trong con


89

Mẫu đề xuất để thực hiện setState trên cha mẹ từ thành phần con là gì.

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)

Tôi có một mảng các mục việc cần làm được duy trì ở trạng thái gốc. Tôi muốn truy cập trạng thái của cha mẹ và thêm một mục todo mới, từ TodoForm's handleClickthành phần. Ý tưởng của tôi là thực hiện một setState trên cha mẹ, nó sẽ hiển thị mục việc cần làm mới được thêm vào.


1
điều này có giúp gì cho stackoverflow.com/questions/24147331/… không?
Dhiraj

Chỉ cần gửi thư rác ở đây ... npmjs.com/package/react-event-observer
jujiyangasli

Tôi đang gặp lỗisetState(...): 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

Tôi đang gặp phải lỗi tương tự rằng tôi không thể setState trên một thành phần chưa được gắn kết. Có giải pháp nào cho việc này không?
Kevin Burton

Câu trả lời:


81

Trong cha của bạn, bạn có thể tạo một hàm giống như hàm addTodoItemnày sẽ thực hiện setState được yêu cầu và sau đó chuyển hàm đó làm đạo cụ cho thành phần con.

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

  ...

});

Bạn có thể gọi addTodoItemtrong handleClick của TodoForm. Thao tác này sẽ thực hiện một setState trên cha sẽ hiển thị mục việc cần làm mới được thêm vào. Hy vọng bạn có được ý tưởng.

Chơi ở đây.


6
Người <<điều hành this.state.todos << todoItem;đang làm gì ở đây?
Gabriel Garrett

@zavtra Tôi đoán là sự nhầm lẫn nhỏ của Ruby đang xảy ra
azium

7
Thực hành không tốt để đột biến this.statetrực tiếp. Tốt hơn nên sử dụng setState chức năng. reactjs.org/docs/react-component.html#setstate
Rohmer

2
fiddle bị hỏng
Hunter Nelson

1
Giải pháp (cập nhật) này sẽ được triển khai như thế nào bằng cách sử dụng React hook?
ecoe

11

Tất cả những điều này về cơ bản đều chính xác, tôi chỉ nghĩ rằng tôi sẽ trỏ đến tài liệu phản ứng chính thức (ish) mới về cơ bản đề xuất: -

Nên có một “nguồn chân lý” duy nhất cho bất kỳ dữ liệu nào thay đổi trong ứng dụng React. Thông thường, trạng thái đầu tiên được thêm vào thành phần cần nó để hiển thị. Sau đó, nếu các thành phần khác cũng cần, bạn có thể nâng nó lên thành tổ tiên chung gần nhất của chúng. Thay vì cố gắng đồng bộ trạng thái giữa các thành phần khác nhau, bạn nên dựa vào luồng dữ liệu từ trên xuống.

Xem https://reactjs.org/docs/lifting-state-up.html . Trang này cũng hoạt động thông qua một ví dụ.


8

Bạn có thể tạo một hàm addTodo trong thành phần mẹ, liên kết nó với ngữ cảnh đó, chuyển nó cho thành phần con và gọi nó từ đó.

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

Sau đó, trong Todos.render, bạn sẽ làm

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

Gọi cái này trong TodoForm với

this.props.addToDo(newTodo);

Điều này rất hữu ích. Nếu không thực hiện bind(this)tại thời điểm truyền chức năng, nó đang ném lỗi không có chức năng như vậy this.setState is not a function.
pratpor

6

Đối với những người đang duy trì trạng thái với React Hook useState, tôi đã điều chỉnh các đề xuất trên để tạo một Ứng dụng trượt demo bên dưới. Trong ứng dụng demo, thành phần thanh trượt con duy trì trạng thái của cha mẹ.

Bản demo cũng sử dụng useEffecthook. (và ít quan trọng hơn, useRefhook)

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;

Bạn có thể sử dụng ứng dụng này với create-react-app và thay thế tất cả mã trong App.js bằng mã ở trên.
NicoWheat

Xin chào, tôi mới phản ứng và đang tự hỏi: Có cần thiết phải sử dụng useEffectkhông? Tại sao chúng ta cần lưu trữ dữ liệu ở cả trạng thái cha và con?
538ROMEO

1
Các ví dụ này không nhằm mục đích chỉ ra lý do tại sao chúng ta cần lưu trữ dữ liệu ở cả phụ huynh và con - hầu hết thời gian bạn không cần. Tuy nhiên, nếu bạn thấy mình ở trong một tình huống mà đứa trẻ nên đặt trạng thái của cha mẹ, thì đây là cách bạn có thể làm. useEffect là cần thiết nếu bạn muốn đặt trạng thái gốc LÀ HIỆU ỨNG của việc thay đổi Trạng thái con.
NicoWheat

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

4
Mặc dù mã này có thể trả lời câu hỏi, nhưng việc cung cấp ngữ cảnh bổ sung về cách thức và / hoặc lý do tại sao nó giải quyết vấn đề sẽ cải thiện giá trị lâu dài của câu trả lời.
Nic3500

2

Tôi đã tìm thấy giải pháp hoạt động và đơn giản sau đây để chuyển các đối số từ thành phần con sang thành phần mẹ:

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

Nhìn vào JSFIDDLE


0

Nếu bạn đang làm việc với một thành phần lớp là cha, một cách rất đơn giản để truyền setState cho con là truyền nó trong một hàm mũi tên. Điều này hoạt động vì nó thiết lập một môi trường được nâng lên có thể được truyền xung quanh:

class ClassComponent ... {

    modifyState = () =>{
        this.setState({...})   
    }
    render(){
          return <><ChildComponent parentStateModifier={modifyState} /></>
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.