ReactJS:子组件内父级的setState

Pav*_*hra 71 reactjs

从子组件在父级上执行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)
Run Code Online (Sandbox Code Playgroud)

我有一系列待办事项,它们保持在父母的状态.我想访问父的状态,并从增加新的待办事项,TodoFormhandleClick组成部分.我的想法是在父级上执行一个setState,它将呈现新添加的待办事项.

Dee*_*pak 64

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

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

  ...

});
Run Code Online (Sandbox Code Playgroud)

你可以addTodoItem在TodoForm的handleClick中调用.这将在父级上执行一个setState,它将呈现新添加的待办事项.希望你明白这个主意.

在这里小提琴

  • `this.state.todos << todoItem;`在这里做什么?```运算符是什么? (6认同)
  • 直接改变`this.state`是不好的做法.最好使用功能的setState.https://reactjs.org/docs/react-component.html#setstate (5认同)
  • 小提琴坏了 (2认同)

Nic*_*eat 12

对于那些使用 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;
Run Code Online (Sandbox Code Playgroud)

  • 这些示例并不是为了说明为什么我们需要在父级和子级中存储数据——大多数时候您不需要这样做。但是,如果您发现自己处于子级应该设置父级状态的情况,那么您可以这样做。如果您想将父状态设置为子状态更改的效果,则 useEffect 是必需的。 (2认同)
  • @IanDunn 我相信 `useCallback` 会阻止在每次渲染时创建新函数。这里很好地解释了何时需要、何时不需要。https://dmitripavlutin.com/dont-overuse-react-usecallback/ (2认同)

dia*_*jin 8

对于使用useStateinsideArrow Functional Components启用的任何人TypeScript,这里有一个简单的示例,说明如何将父级的状态设置器函数传递给子级,并调用它以在适当的时间从子级设置父级的状态 -

样本

父组件:

import {useState} from "react";
const ParentComponent = () => {
  const [hasParentStateChange, setHasParentStateChange] = useState<boolean>(false);

  return (
      <div>
        Parent's view: {String(hasParentStateChange)}
        <ChildComponent
            hasParentStateChange={hasParentStateChange}
            setHasParentStateChange={setHasParentStateChange} // <---- Passing parent's state setter over
        />
      </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

子组件:

interface PropsDefinition {
  hasParentStateChange: boolean;
  setHasParentStateChange(data: boolean): void;
}

const ChildComponent = (props: PropsDefinition) => {
  return (
      <div>
        <div>
          Child's view: {String(props.hasParentStateChange)}
        </div>
        <button
            onClick={() => props.setHasParentStateChange(!props.hasParentStateChange)} // <---- Invoking parent's state setter
        >
            Toggle parent state from child
        </button>
      </div>
  );
}
Run Code Online (Sandbox Code Playgroud)


Tat*_*rne 6

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

对于在React应用程序中发生变化的任何数据,应该有一个"真实来源".通常,首先将状态添加到需要渲染的组件中.然后,如果其他组件也需要它,您可以将它提升到最近的共同祖先.您应该依赖自上而下的数据流,而不是尝试在不同组件之间同步状态.

请参阅https://reactjs.org/docs/lifting-state-up.html.该页面也通过一个例子.


ral*_*all 5

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

// in Todos
addTodo: function(newTodo) {
    // add todo
}
Run Code Online (Sandbox Code Playgroud)

然后,在Todos.render中,你会这样做

<TodoForm addToDo={this.addTodo.bind(this)} />
Run Code Online (Sandbox Code Playgroud)

用TodoForm调用它

this.props.addToDo(newTodo);
Run Code Online (Sandbox Code Playgroud)