【问题标题】:React: updating parent state from nested children componentReact:从嵌套的子组件更新父状态
【发布时间】:2019-02-14 03:56:56
【问题描述】:

我正在使用 React 处理表单。我的想法是创建一个可重用的表单组件,该组件从页面组件中获取状态作为道具,并将保存使用子数据更新其自身状态的逻辑,并将其发送到父页面组件。

页面组件是这样的:

class Page extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: {
        text1: "Initial text1",
        text2: "Initial text2"
      }
    };
  }

  render() {
    return (
      <div className="Page">
        <div className="DataPreview">
          Data preview in Page component
          <div>{this.state.data.text1}</div>
          <div>{this.state.data.text2}</div>
        </div>
        <Form data={this.state.data}>
          <Input id="text1" data={this.state.data.text1} />
          <Input id="text2" data={this.state.data.text2} />
        </Form>
      </div>
    );
  }
}

这是表单组件:

class Form extends Component {
  constructor(props) {
    super(props);
    this.state = this.props.data;
  }

  render() {
    return (
      <div className="Parent">
        <div>Form component</div>
        <div className="DataPreview">
          Data preview in Form component
          <div>{this.state.text1}</div>
          <div>{this.state.text2}</div>
        </div>
        {this.props.children}
      </div>
    );
  }
}

这是 Input 组件:

class Input extends Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div className="Child" id={this.props.id}>
        <div>Input component</div>
        <input id={this.props.id} type="text" value={this.props.data} />
      </div>
    );
  }
}

所以 Input 应该更新 Form 状态,Form 应该更新 Page 状态。我知道当 Input 在 Form 组件内写入时如何传递回调,但我不知道在 Page 组件内写入时如何做,就像在这种情况下一样。

我有一个沙盒供感兴趣的人使用:https://codesandbox.io/s/qx6kqypo09

【问题讨论】:

  • 尝试使用 Redux 或 Flux。这样会更好。
  • 我没说的是我的Page组件是通过Connector组件从Redux获取数据的。但是,在这种情况下,您将如何利用 Redux?
  • 不要遵循这个逻辑。这不是 React 的工作方式。你看不到很多这样的应用程序。如果您需要更新父级的状态,只需像您想的那样使用处理函数即可。另外,为什么你的Input 组件还是个孩子?可以直接在Form组件中使用。
  • 你可以使用 react 'context'
  • @devserkan 这个想法是表单只负责更新输入的数据。我希望它是可重用的,所以我不能在 Form 组件中编写输入。否则,我最终会为我的项目中的每个特定表单使用一个表单组件,这不是 DRY。在我的情况下,如何向子组件添加处理程序函数?

标签: javascript reactjs forms


【解决方案1】:
class Input extends Component {


constructor(props) {
    super(props);
  }

  handleChange(e) {
    let data = this.props.this.state.data;
    data.text1 = e.target.value;
    this.props.this.setState({ data: data });
  }

  render() {
    return (
      <div className="Child" id={this.props.id}>
        <div>Input component {this.props.id}</div>
        <input
          id={this.props.id}
          type="text"
          value={this.props.data}
          onChange={e => this.handleChange(e)}
        />
      </div>
    );
  }
}

使用指定的输入组件和如下所述的页面组件-

class Page extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: {
        text1: "Initial text1",
        text2: "Initial text2"
      }
    };
  }

  render() {
    return (
      <div className="Page">
        <div className="DataPreview">
          Data preview in Page component
          <div>{this.state.data.text1}</div>
          <div>{this.state.data.text2}</div>
        </div>
        <Form data={this.state.data}>
          <Input id="text1" this={this} data={this.state.data.text1} />
          <Input id="text2" data={this.state.data.text2} />
        </Form>
      </div>
    );
  }
}

我认为这会对你有所帮助 谢谢

【讨论】:

  • this 作为道具传递并这样处理状态?我从来没有见过这样的用途。从来没有在好的教程中,即使是在普通的教程中。
  • 我不是在谈论它是否有效。没关系,每个人都有她/他自己的方式。
【解决方案2】:

正如@dashton 所说,我在不同的组件中保持相同的状态,这是不正确的。我将寻找一种不同的方法,而不是仅使用 Form 组件状态,并通过组合共享逻辑。我将为此提出一个新问题。

【讨论】:

    【解决方案3】:

    在不使用某种状态管理的情况下,您需要创建一个方法来处理父组件中的状态更改,然后将其传递给您的子组件一个道具。

    一旦您在子组件中调用该方法,它将更新父组件的状态。

    【讨论】:

      【解决方案4】:

      这是实现您想要实现的目标的一种方法:为onChange 传递一个回调处理程序。但是,当您的应用程序开始变得更大时,事情可能会变得丑陋 :) 如果您正在考虑创建一个复杂的可重用 Form 组件,也许您可​​以检查当前的节点包。

      这个方法的一个替代方案,如果你需要一个简单的,你可以稍微研究一下React Context。它也许可以帮助你。除此之外,Redux 或其他全局状态管理库也可以做到这一点。

      class Page extends React.Component {
      state = {
        data: {
          text1: "Initial text1",
          text2: "Initial text2",
        },
      };
      
      handleChange = ( e ) => {
        const { name, value } = e.target;
        this.setState( prevState => ( {
          data: { ...prevState.data, [ name ]: value },
        } ) );
      }
      
      
      render() {
        return (
          <div className="Page">
            <div className="DataPreview">
                Data preview in Page component
              <div>{this.state.data.text1}</div>
              <div>{this.state.data.text2}</div>
            </div>
            <Form data={this.state.data}>
              <Input name="text1" data={this.state.data.text1} onChange={this.handleChange} />
              <Input name="text2" data={this.state.data.text2} onChange={this.handleChange} />
            </Form>
          </div>
        );
      }
      }
      
      const Form = props => (
        <div className="Parent">
          <div>Form component</div>
          <div className="DataPreview">
            Data preview in Form component
            <div>{props.data.text1}</div>
            <div>{props.data.text2}</div>
          </div>
          {props.children}
        </div>
      );
      
      const Input = props => (
        <div className="Child" id={props.id}>
          <div>Input component {props.id}</div>
          <input name={props.name} type="text" value={props.data} onChange={props.onChange} />
        </div>
      );
      
      const rootElement = document.getElementById("root");
      ReactDOM.render(<Page />, rootElement);
      .Page {
        border: 10px solid blue;
      }
      .Parent {
        border: 10px solid turquoise;
      }
      .Child {
        border: 3px solid tomato;
      }
      .DataPreview {
        border: 3px solid lightgray;
      }
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
      <div id="root"></div>

      【讨论】:

      • 是的,我知道,但我们的想法是在 Form 中设置这个逻辑……
      • 如果是这样,当你的状态在Page 组件中同时在Form 组件中发生变化时会发生什么?哪个州是正确的?如果我理解错了,如果你没有在Form 中保持状态,你为什么要为它的道具分配一个状态?为什么不直接使用道具呢?
      【解决方案5】:

      正如其他人所说,您在不同的组件中保持相同的状态,这显然是不正确的。

      但是,为了满足您关于将子组件与表单分离的要求,您可以使用 render 属性使表单处理输入中的状态更改,该属性会将回调传递给输入,请参阅代码和链接。

      https://codesandbox.io/s/4zyvjm0q64

      import React, { Component } from "react";
      import ReactDOM from "react-dom";
      
      import "./styles.css";
      
      class Input extends Component {
          constructor(props) {
              super(props);
          }
          handleChange(id, value) {
              this.props.onChange(id, value);
          }
          render() {
              return (
                  <div className="Child" id={this.props.id}>
                      <div>Input component {this.props.id}</div>
                      <input
                          id={this.props.id}
                          type="text"
                          value={this.props.data}
                          onChange={e => this.handleChange(e)}
                      />
                  </div>
              );
          }
      }
      
      class Form extends Component {
          constructor(props) {
              super(props);
              this.state = this.props.data;
          }
      
          handleChange = (id, value) => {
              this.setState({ [id]: value });
          };
          render() {
              return (
                  <div className="Parent">
                      <div>Form component</div>
                      <div className="DataPreview">
                          Data preview in Form component
                          <div>{this.state.text1}</div>
                          <div>{this.state.text2}</div>
                      </div>
                      {this.props.render(this.handleChange)}
                  </div>
              );
          }
      }
      
      class Page extends Component {
          constructor(props) {
              super(props);
              this.state = {
                  data: {
                      text1: "Initial text1",
                      text2: "Initial text2"
                  }
              };
          }
      
          render() {
              return (
                  <div className="Page">
                      <div className="DataPreview">
                          Data preview in Page component
                          <div>{this.state.data.text1}</div>
                          <div>{this.state.data.text2}</div>
                      </div>
                      <Form
                          data={this.state.data}
                          render={(handler) => {
                              return (
                                  <div>
                                      <Input id="text1" onChange={e => handler("text1", e.target.value)} />
                                      <Input id="text2" onChange={e => handler("text2", e.target.value)} />
                                  </div>
                              );
                          }}
                      />
                  </div>
              );
          }
      }
      
      const rootElement = document.getElementById("root");
      ReactDOM.render(<Page />, rootElement);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-04-02
        • 2020-05-31
        • 2018-10-09
        • 1970-01-01
        • 2017-09-28
        • 1970-01-01
        • 2018-07-20
        相关资源
        最近更新 更多