【问题标题】:Access prop outside of class in React when calling Higher Order Component调用高阶组件时在 React 中访问类外的道具
【发布时间】:2017-02-14 14:52:52
【问题描述】:

我正在尝试使用高阶组件(HOC)模式来重用一些连接到状态并使用 Redux Form formValueSelector 方法的代码。

formValueSelector 需要一个引用表单名称的字符串。我想动态设置它,并能够在我需要项目值的时候使用这个 HOC。我使用项目值进行计算。

在下面的代码中,HOC 被传递了组件和一个字符串。我想将此设置为从父(表单)传入的道具formName。

我是 HOC 模式的新手,因此非常感谢任何提示。

HOC

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { formValueSelector } from 'redux-form';

function FormItemsValueSelectorHOC(FormElement, formName) {
  const selector = formValueSelector(formName);
  @connect(state => {
    console.log(state);
    const items = selector(state, 'items');
    return {
      items
    };
  }, null)
  class Base extends Component {
    render() {
      return (
        <FormElement {...this.props} />
      );
    }
  }
  return Base;
}
export default FormItemsValueSelectorHOC;

包装组件

import React, { Component, PropTypes } from 'react';
import { Field } from 'redux-form';
import formItemsValueSelectorHOC from '../Utilities/FormItemsValueSelectorHOC';

const renderField = ({ placeholder, input, type}) => {
  return (
    <input
      {...input}
      placeholder={placeholder}
      type={type}
    />
  );
};

class StatementLineItemDesktop extends Component {
  static propTypes = {
    items: PropTypes.array.isRequired,
    index: PropTypes.number.isRequired,
    item: PropTypes.string.isRequired,
    fields: PropTypes.object.isRequired,
    formName: PropTypes.string.isRequired
  };

  calculateLineTotal(items, index) {
    let unitPrice = '0';
    let quantity = '0';
    let lineTotal = '0.00';
    if (items) {
      if (items[index].price) {
        unitPrice = items[index].price.amountInCents;
      }
      quantity = items[index].quantity;
    }
    if (unitPrice && quantity) {
      lineTotal = unitPrice * quantity;
      lineTotal = Number(Math.round(lineTotal+'e2')+'e-2').toFixed(2); 
    }
    return <input value={lineTotal} readOnly placeholder="0.00" />;
  }

  render() {
    const { items, index, item, fields, formName} = this.props;
    return (
      <tr id={`item-row-${index}`} key={index} className="desktop-only">
        <td>
          <Field
            name={`${item}.text`}
            type="text"
            component={renderField}
            placeholder="Description"
          />
        </td>
        <td>
          <Field
            name={`${item}.quantity`}
            type="text"
            component={renderField}
            placeholder="0.00"
          />
        </td>
        <td>
          <Field
            name={`${item}.price.amountInCents`}
            type="text"
            component={renderField}
            placeholder="0.00"
          />
        </td>
        <td className="last-col">
          <Field
            name={`${item}.price.taxInclusive`}
            type="hidden"
            component="input"
          />
          {::this.calculateLineTotal(items, index)}
          <a
            className="remove-icon"
            onClick={() => fields.remove(index)}
          >
            <span className="icon icon-bridge_close" />
          </a>
        </td>
      </tr>
    );
  }
}

export default formItemsValueSelectorHOC(StatementLineItemDesktop, 'editQuote');

【问题讨论】:

  • 不确定我是否清楚,但是您是否考虑过仅导出反应组件StatementLineItemsDesktop 类并将此反应组件与formItemsValueSelectorHOC 函数一起导入父组件所在的文件中声明这样你就可以在你父母的 render() 方法中使用 this.props.formName 作为第二个参数来调用这个 HOC?

标签: javascript reactjs react-redux redux-form


【解决方案1】:

TLDR:使用ownProps参数

你应该做什么的草稿

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { formValueSelector } from 'redux-form';

function FormItemsValueSelectorHOC(FormElement) {
  @connect((state, ownProps) => {
    const formName = ownProps.formName;
    const selector = formValueSelector(formName);
    const items = selector(state, 'items');
    return {
      items
    };
  }, null)
  class Base extends Component {
    render() {
      // Now in here you should omit `formName` from the props you are
      // passing to your Form Element since it's not used overthere
      return (
        <FormElement {...this.props} />
      );
    }
  }
  return Base;
}
export default FormItemsValueSelectorHOC;

这将是您创建连接组件的方式

formItemsValueSelectorHOC(StatementLineItemDesktop);

这将是你使用它的方式

<ConnectedStatementLineItemDesktop formName={"editQuote"} />

让我再解释一下这是如何工作的

您缺少的是 React-Redux API,您可能应该更多地探索它,因为它已经考虑了很多这样的用例

所以,React-Redux 的connect 函数的第一个参数叫做mapStateToProps

这是它的签名:

mapStateToProps(state, [ownProps]): stateProps

我要说的是ownProps参数。

ownProps 包含传递给连接组件的所有道具。

只是为了澄清,你有这些组件

  • 常规组件:即StatementLineItemDesktopBase
  • 连接的组件:即ConnectedBase = connect(mapStateToProps)(Base)

因此,根据这些信息,您的名为 FormItemsValueSelectorHOC 的 HOC 函数会返回 ConnectedBase 的变体。

因此,无论您传递给ConnectedBase 的任何道具或从FormItemsValueSelectorHOC 返回的任何组件,您都可以从ownProps 访问它们

顺便说一句,在您的特定情况下,这是您的 mapStateToProps

function mapStateToProps(state, ownProps) {
  const formName = ownProps.formName;
  const selector = formValueSelector(formName);
  const items = selector(state, 'items');
  return {
    items
  };
}

需要注意的是connect 也是一个 HOC,所以从逻辑上讲,你可以用普通组件做的大部分事情,你也可以用连接的组件做,我建议你阅读connect 来源,它不长也不难,因此您可能可以探索和理解更多这个答案。

我希望这会有所帮助。

【讨论】:

  • 回答得好!!
  • 谢谢。这有助于解决我如何动态设置 formName 和表单字段名称的问题。
猜你喜欢
  • 2018-12-30
  • 1970-01-01
  • 2017-05-16
  • 2016-12-08
  • 2021-09-15
  • 2020-05-04
  • 2019-10-31
  • 2021-04-14
  • 2018-04-03
相关资源
最近更新 更多