ReactJS:子组件内部父级上的setState


89

在子组件的父项上执行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)

我有一个待办事项数组,保持在父母的状态。我想访问父级的状态,并从TodoFormhandleClick组件中添加一个新的待办事项。我的想法是在父项上执行setState,这将呈现新添加的待办事项。


1
这对stackoverflow.com/questions/24147331 / ...有帮助吗?
Dhiraj 2015年

只是垃圾邮件在这里... npmjs.com/package/react-event-observer
jujiyangasli

我遇到了错误setState(...): 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

我收到了相同的错误,无法在未安装的组件上设置状态。是否有解决方法?
凯文·伯顿

Answers:


81

在您的父级中,您可以创建一个类似的函数addTodoItem,该函数将执行所需的setState,然后将该函数作为prop传递给子组件。

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

  ...

});

您可以addTodoItem在TodoForm的handleClick中调用。这将在父对象上执行setState,这将呈现新添加的待办事项。希望你明白。

在这里摆弄。


6
什么是<<在运营商this.state.todos << todoItem;在这里做什么?
加百利·加勒特

@zavtra我猜想小红宝石会持续

7
this.state直接变异是不明智的做法。最好使用功能性setState。reactjs.org/docs/react-component.html#setstate
罗默(Rohmer)

2
小提琴被打破了
纳尔逊猎人

1
如何使用React钩子实现这个(更新的)解决方案?
ecoe

11

这些基本上都是正确的,我只是想指出一些新的(ish)官方反应文档,该文档基本上建议:

对于React应用程序中发生的任何数据更改,应该只有一个“事实来源”。通常,首先将状态添加到需要呈现的组件。然后,如果还需要其他组件,则可以将其提升到最接近的共同祖先。与其尝试在不同组件之间同步状态,不如依靠自上而下的数据流。

参见https://reactjs.org/docs/lifting-state-up.html。该页面还通过一个示例工作。


8

您可以在父组件中创建addTodo函数,将其绑定到该上下文,将其传递给子组件,然后从那里调用它。

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

然后,在Todos.render中,您将

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

在TodoForm中用

this.props.addToDo(newTodo);

这非常有用。没有bind(this)通过函数的时候,没有这样的函数就抛出了错误this.setState is not a function
pratpor

6

对于那些使用React Hook保持状态的人useState,我根据上述建议修改了下面的演示滑块应用程序。在演示应用程序中,子滑块组件将保持父状态。

该演示还使用了useEffect挂钩。(更不重要的是,useRef钩子)

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;

您可以将此应用程序与create-react-app一起使用,并将App.js中的所有代码替换为上面的代码。
NicoWheat

嗨,我是新来的,我想知道:是否有必要使用useEffect?为什么我们需要将数据存储在父状态和子状态中?
538ROMEO

1
这些示例无意显示为什么我们在大多数时间不需要在父级和子级中存储数据。但是,如果您发现自己处于孩子应设置父状态的情况,这就是您可以采取的方法。如果要将父状态设置为childState更改的效果,则useEffect是必需的。
NicoWheat

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

4
尽管此代码可以回答问题,但提供有关如何和/或为什么解决问题的其他上下文将提高​​答案的长期价值。
Nic3500 '18

2

我发现了以下可行且简单的解决方案,可将参数从子组件传递到父组件:

//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


0

如果您将类组件作为父组件使用,则将setState传递给子组件的一种非常简单的方法是在箭头函数中传递它。这在设置可以传递的提升环境时起作用:

class ClassComponent ... {

    modifyState = () =>{
        this.setState({...})   
    }
    render(){
          return <><ChildComponent parentStateModifier={modifyState} /></>
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.