Хорошо, я смог добиться этого, и это было крайне нетривиально.
Переходя от кода в приведенном выше вопросе, я добавил это к 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:
this.props
внутри AuthFormInput
становится this.props
внутри HOC
- послушайте
this.props.meta
и this.props.input
- если поле затронуто и имеет ошибку, обновите Redux
- если поле затронуто и больше не имеет ошибки, сотрите его из 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
getFormMeta
селектор сделал это за меня redux-form.com/7.4.2 /docs/api/selectors.md - person llamerr   schedule 11.03.2019