Передача компонента Props дочернему компоненту из родительского элемента с помощью cloneElement - PullRequest
0 голосов
/ 03 ноября 2018

В приведенном ниже коде, когда флажок установлен в AddressWrapper, ввод данных отправителя в AddressForm должен быть отключен. Я не могу понять, почему AddressWrapper cloneElement не передает свое состояние дочернему элементу. Я проверил много ссылок на эту проблему и, насколько я могу судить, это должно работать. Это самый близкий способ передачи реквизита {this.props.children} этой проблеме, но он использует обратный вызов от дочернего элемента к родительскому, и мне нужно изменить родительское состояние для обновления дочернего элемента. Я мог бы использовать публикацию / подписку, но я пытаюсь сделать это способом «Реагировать».

class AddressForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      firstName: "Joyce",
      disableInputs: props.billToSameAsShipTo
    };
    this.handleBillToSameAsShipToChanged = this.handleBillToSameAsShipToChanged.bind(
      this
    );
  }

  handleBillToSameAsShipToChanged() {
    this.setState({ billToSameAsShipTo: !this.state.billToSameAsShipTo });
  }

  handleFirstNameChanged(ev) {
    this.setState({ firstName: ev.target.value });
  }

  render() {
    return (
      <form>
        <div className="form-row">
          <div className="col-6">
            <input
              type="text"
              className="form-control"
              placeholder="First name"
              disabled={this.state.disableInputs}
              value={this.state.firstName}
              onChange={this.handleFirstNameChanged.bind(this)}
            />
          </div>
        </div>
      </form>
    );
  }
}

class AddressFormWrapper extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      billToSameAsShipTo: true
    };
    this.handlebillToSameAsShipToChanged = this.handlebillToSameAsShipToChanged.bind(
      this
    );
  }

  handlebillToSameAsShipToChanged() {
    this.setState({ billToSameAsShipTo: !this.state.billToSameAsShipTo });
  }

  render() {
    const billToSameAsShipTo = () => {
      if (this.props.showSameAsShipTo === true) {
        return (
          <span style={{ fontSize: "10pt", marginLeft: "20px" }}>
            <input
              type="checkbox"
              checked={this.state.billToSameAsShipTo}
              onChange={this.handlebillToSameAsShipToChanged}
            />
            &nbsp;
            <span>Same as Ship To</span>
          </span>
        );
      }
    };

    const childWithProp = React.Children.map(this.props.children, child => {
      return React.cloneElement(child, { ...this.state });
    });

    return (
      <span className="col-6">
        <h3>
          {this.props.title}
          {billToSameAsShipTo()}
        </h3>
        <span>{childWithProp}</span>
      </span>
    );
  }
}

const Checkout = () => {
  return (
    <div>
      <br />
      <br />
      <div className="row">
        <AddressFormWrapper title="Ship To" showSameAsShipTo={false}>
          <span className="col-6">
            <AddressForm />
          </span>
        </AddressFormWrapper>

        <AddressFormWrapper title="Bill To" showSameAsShipTo={true}>
          <span className="col-6">
            <AddressForm />
          </span>
        </AddressFormWrapper>
      </div>
    </div>
  );
};

1 Ответ

0 голосов
/ 03 ноября 2018

В AddressFormWrapper вы наносите на карту детей и пропускаете реквизиты с cloneElement().

Согласно DOCS :

Вызывает функцию для каждого непосредственного ребенка, содержащегося в потомках ...

Но посмотрите, кто эти (непосредственные) дети AddressFormWrapper:

<AddressFormWrapper title="Bill To" showSameAsShipTo={true}>
  <span className="col-6">
    <AddressForm />
  </span>
</AddressFormWrapper>

В данном случае это элемент span, а не AddressForm.

Если вы отрендерите это так, оно будет работать как положено:

<AddressFormWrapper title="Bill To" showSameAsShipTo={true}>
    <AddressForm />
</AddressFormWrapper>

Еще одна вещь, на которую следует обратить внимание, в AddressForm вы устанавливаете состояние:

disableInputs: props.billToSameAsShipTo

Это внутри constructor, и он будет работать только один раз. Таким образом, он получит начальное значение, но не будет изменен.
Либо обновите его в componentDidUpdate , либо лучше просто используйте реквизиты напрямую:

disabled={this.props.billToSameAsShipTo}

Вот пример:

class AddressForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      firstName: "Joyce",
      disableInputs: props.billToSameAsShipTo
    };
    this.handleBillToSameAsShipToChanged = this.handleBillToSameAsShipToChanged.bind(
      this
    );
  }

  handleBillToSameAsShipToChanged() {
    this.setState({ billToSameAsShipTo: !this.state.billToSameAsShipTo });
  }

  handleFirstNameChanged(ev) {
    this.setState({ firstName: ev.target.value });
  }

  billToSameAsShipTo() {
    if (this.props.showSameAsShipTo === true) {
      return (
        <span style={{ fontSize: "10pt" }}>
          <input
            type="checkbox"
            checked={this.state.billToSameAsShipTo}
            onChange={this.handleBillToSameAsShipToChanged}
          />&nbsp;<span>Same as Ship To</span>
        </span>
      );
    }
  }

  render() {
    return (
      <form>
        <div className="form-row">
          <div className="col-6">
            <input
              type="text"
              className="form-control"
              placeholder="First name"
              disabled={this.props.billToSameAsShipTo}
              value={this.state.firstName}
              onChange={this.handleFirstNameChanged.bind(this)}
            />
          </div>
        </div>
      </form>
    );
  }
}

class AddressFormWrapper extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      billToSameAsShipTo: true
    };
    this.handlebillToSameAsShipToChanged = this.handlebillToSameAsShipToChanged.bind(
      this
    );
  }

  handlebillToSameAsShipToChanged() {
    this.setState({ billToSameAsShipTo: !this.state.billToSameAsShipTo });
  }

  render() {
    const billToSameAsShipTo = () => {
      if (this.props.showSameAsShipTo === true) {
        return (
          <span style={{ fontSize: "10pt", marginLeft: "20px" }}>
            <input
              type="checkbox"
              checked={this.state.billToSameAsShipTo}
              onChange={this.handlebillToSameAsShipToChanged}
            />&nbsp;<span>Same as Ship To</span>
          </span>
        );
      }
    };

    const childWithProp = React.Children.map(this.props.children, child => {
      return React.cloneElement(child, { ...this.state });
    });

    return (
      <span className="col-6">
        <h3>
          {this.props.title}
          {billToSameAsShipTo()}
        </h3>
        <span>{childWithProp}</span>
      </span>
    );
  }
}

const Checkout = () => {
  return (
    <div>
      <br />
      <br />
      <div className="row">
        <AddressFormWrapper title="Ship To" showSameAsShipTo={false}>
          <span className="col-6">
            <AddressForm />
          </span>
        </AddressFormWrapper>

        <AddressFormWrapper title="Bill To" showSameAsShipTo={true}>
          <AddressForm />
        </AddressFormWrapper>
      </div>
    </div>
  );
};

ReactDOM.render(<Checkout />, document.querySelector("#app"));
<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="app"/>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...