Доступ к реквизиту вне класса в React при вызове компонента более высокого порядка

Я пытаюсь использовать шаблон компонента высшего порядка (HOC) для повторного использования некоторого кода, который подключается к состоянию и использует метод formValueSelector Redux Form.

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

В приведенном ниже коде HOC передается компонент и строка. Я хотел бы установить это в prop 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');

person Dom Hede    schedule 06.10.2016    source источник
comment
Не уверен, что это ясно для меня, но думали ли вы об экспорте только класса StatementLineItemsDesktop компонента реакции и импорте этого компонента React вместе с функцией formItemsValueSelectorHOC в файл, где объявлен ваш родительский компонент, чтобы вы могли вызвать этот HOC в методе render() вашего родителя используя this.props.formName в качестве второго аргумента?   -  person dzv3    schedule 06.10.2016


Ответы (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"} />

Позвольте мне объяснить немного больше, как это работает

Чего вам не хватало, так это API React-Redux, вам, вероятно, следует изучить его больше, потому что он уже рассматривает множество вариантов использования.

Итак, первый параметр функции connect React-Redux называется mapStateToProps.

Это его подпись:

mapStateToProps(state, [ownProps]): stateProps

То, что я хочу отметить, это параметр ownProps.

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

Просто для пояснения, у вас есть эти компоненты

  • Обычный компонент: т. е. StatementLineItemDesktop и Base
  • Подключенный компонент: т. е. ConnectedBase = connect(mapStateToProps)(Base)

Итак, исходя из этой информации, ваша функция HOC с именем FormItemsValueSelectorHOC возвращает вариант 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, это не долго и не сложно, так что вы, вероятно, сможете изучить и понять больше этого ответа.

Надеюсь, это помогло.

person franleplant    schedule 07.10.2016
comment
Хорошо ответил!! - person Dom Hede; 12.10.2016
comment
Спасибо за это. Это помогает решить мою проблему, связанную с тем, как я могу динамически устанавливать имена полей formName и form. - person Noah John Ucab; 04.04.2018