javascript - 使用 cloneElement 将 Props 从父组件传递给子组件

标签 javascript reactjs components

在下面的代码中,当在 AddressWrapper 中选中复选框时,应禁用 AddressForm 中的 Ship To 输入。我不明白为什么 AddressWrapper cloneElement 没有将它的状态传递给 child 。我已经检查了很多关于这个问题的链接,据我所知这应该有效。这是最近的 How to pass props to {this.props.children}对于这个问题,但它正在使用从 child 到 parent 的回调,我需要改变 parent 的状态来更新 child 。我可以使用发布/订阅来完成它,但我正在尝试以“React”方式进行。

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

最佳答案

AddressFormWrapper 中,您映射到子项并使用 cloneElement() 传递 Prop 。

根据 DOCS :

Invokes a function on every immediate child contained within children...

但仔细看看 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 中更新它或者更好的是直接使用 Prop :

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"/>

关于javascript - 使用 cloneElement 将 Props 从父组件传递给子组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53132761/

相关文章:

javascript - React js中增量值的计算

javascript - 响应式 Durandal 对话框

reactjs - 为 React Native TextInput 创建 ref 时的 TypeScript 问题

javascript - react 性能调试器

java - Java 中的连通分量标记

java - JTable,如何设置不同的侧边框

javascript - MongoDB中数据的反规范化

javascript - 如何获得计算样式?

具有覆盖值的 Javascript 订单函数

reactjs - 使用变量名称渲染 React 组件