React:从嵌套子组件更新父状态

问题描述 投票:0回答:5

我正在使用React的形式工作。我的想法是创建一个可重用的Form组件,它将来自Page组件的状态作为props,并将保存用子数据更新自己状态的逻辑,并将其发送到父页面组件。

Page组件是这样的:

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

这是Form组件:

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

而这个输入组件:

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写入Inside Form组件时传递一个回调,但是当我在Page组件内部写入时,我无法弄清楚如何执行它,就像在这种情况下一样。

我有一个感兴趣的沙箱:https://codesandbox.io/s/qx6kqypo09

javascript reactjs forms
5个回答
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>
    );
  }
}

我想这会对你有所帮助


1
投票

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


0
投票

如果不使用某种状态管理,则需要创建一个方法来处理父组件中的状态更改,然后将其传递给子组件作为prop。

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


0
投票

这是实现您想要实现的目标的一种方式:为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>

0
投票

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

但是,要回答关于从表单中解耦子组件的要求,可以使用render prop使表单处理来自输入的状态更改,这将使回调传递给输入,请参阅代码和链接。

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);
© www.soinside.com 2019 - 2024. All rights reserved.