简体   繁体   English

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

[英]React: updating parent state from nested children component

I'm working in a form with React. 我正在与React合作。 My idea is to create a reusable Form component that gets the state from a Page component as props, and will hold the logic for updating its own state with children data, send it to parent Page component. 我的想法是创建一个可重用的Form组件,该组件从Page组件获取状态作为道具,并将保留使用子数据更新其自身状态的逻辑,并将其发送给父Page组件。

The Page component is this: 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>
    );
  }
}

This is the Form component: 这是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>
    );
  }
}

And this the Input component: 这是输入组件:

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

So Input should update Form state, and Form should update Page state. 因此,Input应该更新Form状态,而Form应该更新Page状态。 I know how to do it passing a callback when the Input is written Inside Form component, but I cant figure out how to do it when it is written inside Page component, like in this case. 我知道如何将Input编写在Form组件内部时传递回调,但是无法弄清楚如何将其编写在Page组件内部,就像在这种情况下那样。

I have a Sandbox for those interested: https://codesandbox.io/s/qx6kqypo09 我为感兴趣的人提供了一个沙箱: https : //codesandbox.io/s/qx6kqypo09

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

use your input component as specified and your page component as mentioned below- 使用指定的输入组件和如下所述的页面组件-

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

I think this will help you Thanks 我认为这将对您有所帮助

As @dashton said, I am holding the same state in different components, and that's not correct. 正如@dashton所说,我在不同的组件中保持相同的状态,这是不正确的。 I will look for a different approach instead using only Form component state, and sharing logic via composition. 我将寻找一种不同的方法,而不是仅使用Form组件状态,并通过组合共享逻辑。 I will open a new question for this. 我将为此提出一个新问题。

without using some kind of state management, you would need to create a method that handles the state change in the parent component that you would then pass down to your child component aa prop. 在不使用某种状态管理的情况下,您需要创建一个方法来处理父组件中的状态更改,然后将其传递给子组件。

Once you call that method in the child component it will update the state of the parent component. 在子组件中调用该方法后,它将更新父组件的状态。

This is one way of doing what you want to achieve: passing a callback handler for onChange . 这是您要实现的一种方法:为onChange传递一个回调处理程序。 But, when your app starts to get bigger things can be ugly :) If you are thinking about creating a complex reusable Form component maybe you can examine the present node packages. 但是,当您的应用开始变大时,可能会很难看:)如果您正在考虑创建一个复杂的可重用Form组件,则可以检查当前的节点包。

An alternative to this method, if you need a simple one, you can study React Context a little bit. 此方法的替代方法,如果您需要一个简单的方法,则可以学习一下React Context。 It can help you maybe. 它可以帮助您。 Other than that Redux or other global state management libraries can do this also. 除了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> 

As other people have said, you are holding the same state in different components, which obviously isn't correct. 正如其他人所说,您在不同组件中拥有相同的状态,这显然是不正确的。

However, to answer your requirement regarding decoupling child components from the form, you could make your form handle state changes from the inputs by using a render prop which would pass a callback to the inputs, see code and link. 但是,要满足有关将子组件与表单分离的要求,您可以使用渲染道具使表单处理输入的状态更改,该道具将回调传递给输入,请参见代码和链接。

https://codesandbox.io/s/4zyvjm0q64 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM