Реакция: обновление родительского состояния из вложенного дочернего компонента - PullRequest
0 голосов
/ 09 сентября 2018

Я работаю в форме с 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>
    );
  }
}

Таким образом, ввод должен обновлять состояние формы, а форма должна обновлять состояние страницы. Я знаю, как сделать это, передавая обратный вызов, когда ввод написан внутри компонента Form, но я не могу понять, как это сделать, когда он написан внутри компонента Page, как в этом случае.

У меня есть песочница для интересующихся: https://codesandbox.io/s/qx6kqypo09

Ответы [ 5 ]

0 голосов
/ 10 сентября 2018

Как сказал @dashton, я держу одно и то же состояние в разных компонентах, и это не правильно. Я буду искать другой подход, вместо этого используя только Form состояние компонента и разделяя логику с помощью композиции. Я открою новый вопрос для этого.

0 голосов
/ 10 сентября 2018

Как говорили другие люди, вы держите одно и то же состояние в разных компонентах, что, очевидно, неверно.

Однако, чтобы удовлетворить ваше требование относительно отделения дочерних компонентов от формы, вы могли бы заставить вашу форму обрабатывать изменения состояния от входных данных с помощью 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);
0 голосов
/ 09 сентября 2018

без использования какого-либо управления состоянием вам нужно будет создать метод, который будет обрабатывать изменение состояния в родительском компоненте, который затем будет передан вашему дочернему компоненту в качестве опоры.

Как только вы вызовете этот метод в дочернем компоненте, он обновит состояние родительского компонента.

0 голосов
/ 09 сентября 2018

Это один из способов сделать то, что вы хотите достичь: передать обработчик обратного вызова для 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 голосов
/ 09 сентября 2018
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>
    );
  }
}

Я думаю, что это поможет вам Спасибо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...