Как с помощью Redux-Form V7 получить доступ к тем полям, которые в данный момент затронуты?

У меня есть форма (назовем ее LoginContainer), в которой есть компонент <Field />:

import { reduxForm, Field } from 'redux-form'
...
class LoginContainer extends Component {
...

render() {
  const { handleSubmit, pristine, submitting } = this.props

  return (
  ...

  <Field
    component={AuthFormInput}
    type="password"
    name="person_password"
    placeholder="Password"
  />

AuthFormInput.jsx

const AuthFormInput = ({
  input,
  name,
  placeholder,
  type,
  meta: { touched, error },
}) => {
  return [
    <div className="row-md" key={`${name}_field`}>
      <input
        placeholder={placeholder}
        name={name}
        type={type}
        className={maybeHasError}
        {...input}
      />
    </div>, // error display works amazing here, no problem
    <div key={`${name}_error`}>{touched && (error && <span className="edit_required">{error}</span>)}</div>,
  ]
}

Проблема в том, что я пытаюсь обрабатывать ошибки вне компонента AuthFormInput и не могу найти решения, позволяющего мне получить доступ к состоянию touched каждого поля из любого места, кроме AuthFormInput, которое абстрагируется внутри <Field />.

Есть ли способ получить доступ к meta.touched изнутри LoginContainer?

Redux-Form, похоже, не имеет какой-либо функции getTouchedFields(), которую я мог бы использовать.

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

Я не могу найти способ передать данные из AuthFormInput в LoginContainer, потому что между ними находится <Field />, который внутренне контролируется Redux-Form.

Есть ли способ сделать это с помощью Redux-Form V7?

Есть ли способ создать HOC, который позволит мне отслеживать meta.touched внутри <Field />?


person agm1984    schedule 14.11.2017    source источник
comment
Я только что завернул поле в HOC и вижу обрывки доказательств того, что это сработает.   -  person agm1984    schedule 14.11.2017
comment
Я не уверен, что это подходит для вас, но у меня была аналогичная проблема, когда мне нужно было получить доступ к значению полей (тронутых или нет) на уровне формы. getFormMeta селектор сделал это за меня redux-form.com/7.4.2 /docs/api/selectors.md   -  person llamerr    schedule 11.03.2019


Ответы (1)


Хорошо, я смог добиться этого, и это было крайне нетривиально.

Переходя от кода в приведенном выше вопросе, я добавил это к LoginContainer:

import withFormListener from './withFormListener'

const listeningAuthFormInput = withFormListener(AuthFormInput)

Итак, <Field /> стало таким:

<Field
  component={listeningAuthFormInput}
  type="password"
  name="person_password"
  placeholder="Password"
/>

HOC оборачивает поле ввода текста (или любое поле, которое вы хотите) и просто слушает изменения в свойствах, а затем обновляет Redux:

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { updateField } from './auth_actions'

const withFormListener = function listenToFormField(FormField) {
  class WrappedField extends Component {
    handleFieldUpdate() {
      const { name } = this.props.input
      const { touched, error } = this.props.meta
      if (touched && error) {
        if (this.props[`error_${name}`] === error) return null
        return this.props.updateField({ prop: name, value: error })
      }
      if ((touched && !error) && this.props[`error_${name}`].length) {
        return this.props.updateField({ prop: name, value: '' })
      }
      return null
    }
    componentDidUpdate() {
      this.handleFieldUpdate()
    }
    render() {
      return <FormField {...this.props} />
    }
  }
  const mapStateToProps = ({
    auth: { error_person_tel, error_person_password },
  }) => ({
    error_person_tel, error_person_password,
  })
  return connect(mapStateToProps, { updateField })(WrappedField)
}

export default withFormListener

Логически, что происходит в HOC:

  1. this.props внутри AuthFormInput становится this.props внутри HOC
  2. послушайте this.props.meta и this.props.input
  3. если поле затронуто и имеет ошибку, обновите Redux
  4. если поле затронуто и больше не имеет ошибки, сотрите его из Redux

Вы должны увидеть моего создателя действия и редюсер из LoginContainer, чтобы не было путаницы.

Создатель действий

export const updateField = ({ prop, value }) => ({
  type: AUTH_UPDATE_FIELD,
  payload: { prop, value },
})

Редуктор

Я рекомендую сохранять объект состояния плоским. Я экспериментировал с вложением здесь, например:

const INITIAL_STATE = {
  errors: { field1, field2 }
}

Но это доставляло мне серьезные проблемы с обновлением состояния из-за соображений равенства и сложности с предотвращением перезаписи errors. Это намного противнее, чем кажется. Поэтому остановился на этом:

const INITIAL_STATE = {
  ...
  error_person_tel: '',
  error_person_password: '',
}

case AUTH_UPDATE_FIELD: {
  const { prop, value } = action.payload
  return {
    ...state,
    [`error_${prop}`]: value,
  }
}

Вы можете прочитать больше о нормализации состояния здесь (т. е. о выравнивании): https://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html

Я вообще не занимался оптимизацией, но вот как я отображаю ошибки в настоящее время (внутри LoginContainer):

<ServerErrors
  errors={[
    this.props.error_person_tel,
    this.props.error_person_password,
  ].filter(val => val)}
/>

Это простой компонент, который отображает либо null, либо массив строк ошибок.

Я настоятельно рекомендую использовать встроенную обработку ошибок поля Redux-Form. Это прекрасно работает. К сожалению, мне пришлось абстрагировать элемент управления от уровня поля, чтобы выполнить дизайн пользовательского интерфейса, так что это было единственным решением в V7.

Я искал неделями. Я задал еще один вопрос о переполнении стека, и он не получил активности, как и этот. Я рад, что смог добиться работающего решения.

Я также настоятельно рекомендую вам взглянуть на Formik. Несколько недель назад я использовал его в React Native для выполнения этого требования к пользовательскому интерфейсу. Скорее всего, вы обнаружите, что это работает очень хорошо. Поговорите с Jared Palmer в Твиттере, если у вас возникнут какие-либо проблемы с ним. Formik может похвастаться приростом производительности по сравнению с Redux-Form благодаря более интеллектуальной архитектуре. Я очень доволен этим в React Native.

Если вы решитесь пойти по этому пути, будьте крайне осторожны, чтобы не вызвать бесконечные циклы, вызванные setState. Я провел часы, часы и часы исследований, проб и ошибок, чтобы сделать это :) Я вполне доволен краткостью решения. Пожалуйста, дайте мне знать, если у вас есть лучший способ.

person agm1984    schedule 14.11.2017