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

时间:2018-09-09 14:32:50

标签: javascript reactjs forms

我正在与React一起工作。我的想法是创建一个可重用的Form组件,该组件从Page组件获取状态作为道具,并将保留使用子数据更新其自身状态的逻辑,并将其发送给父Page组件。

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

这是表单组件:

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写入Form组件内部时通过回调传递信息,但是像这种情况一样,我无法弄清楚如何将其写入Page组件内部。

我为有兴趣的人提供了一个沙箱:https://codesandbox.io/s/qx6kqypo09

5 个答案:

答案 0 :(得分: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 :(得分:1)

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

答案 2 :(得分:0)

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

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

答案 3 :(得分: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>

答案 4 :(得分:0)

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

但是,要满足您关于将子组件与表单分离的要求,您可以使用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);
相关问题