Подтвердить что ты не робот

Редукционная форма дескриптора: как получить доступ к состоянию хранилища?

В функции редукции handleSubmit, которая делает вызов Ajax, необходимы некоторые свойства из состояния Redux в Ajax (например, идентификатор пользователя).

Это было бы достаточно легко, если бы я хотел определить handleSubmit в компоненте формы: просто позвоните mapStateToProps, чтобы передать все, что мне нужно, и прочитайте его из this.props в моей handleSubmit.

Однако, как хороший разработчик React-Redux, я пишу отдельные компоненты представления и контейнеры, поэтому мои компоненты просмотра могут быть простыми с небольшим или вообще отсутствующим поведением. Поэтому я хочу определить handleSubmit в контейнере.

Опять же, для этого создана простая-редукционная форма. Определите onSubmit в mapDispatchToProps, и форма будет вызывать его автоматически.

За исключением, о, подождите, в mapDispatchToProps нет доступа к состоянию редукции.

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

Хмм, подождите, невозможно передать любые данные в handleSubmit с помощью этого механизма! Вы получаете один параметр values, и нет возможности добавить еще один параметр.

Это оставляет следующие непривлекательные альтернативы (из которых я могу проверить, что работают 1 и 2):

1 Определите обработчик отправки в компоненте формы и оттуда вызовите мою собственную функцию отправки отправителя, переданную из mapDispatchToProps. Это нормально, но мой компонент должен знать некоторые реквизиты из состояния редукции, которые не обязаны отображать форму. (Эта проблема не уникальна для редукционной формы.)

dumbSubmit(values)
{
  const { mySubmitHandler, user} = this.props;
  mySubmitHandler(values, user);
}

<form onSubmit={ handleSubmit(this.dumbSubmit.bind(this)) }>

Или, более кратко, все это можно объединить в функцию стрелки:

<form onSubmit={ handleSubmit((values)=>{mySubmitHandler(values, this.props.user);}

Затем, чтобы сделать этот обработчик полностью агностиком, он может просто передать все this.props обратно в пользовательский обработчик:

<form onSubmit={ handleSubmit((values)=>{mySubmitHandler(values, this.props);}

2 Определить onSubmit в mergeProps вместо mapDispatchToProps, чтобы он имел доступ к stateProps. Дэн Абрамов рекомендует не использовать mergeProps (по соображениям производительности), поэтому это кажется субоптимальным.

  function mergeProps(stateProps, dispatchProps, ownProps) {
  const { user } = stateProps.authReducer;
  const { dispatch } = dispatchProps;
  return {
    ...ownProps,
    ...dispatchProps,
    ...stateProps,
     onSubmit: (values) => {
       const { name, description } = values;
       const thing = new Thing(name, description, []);
       dispatch(createNewThing(thing, user.id));
     }
  }

3 Скопировать свойства состояния в поля редукционной формы, которые не отображаются на любые элементы управления в компоненте формы. Это гарантирует, что они доступны в параметре values, переданном на handleSubmit. Это похоже на хак.

Там должен быть лучший способ!

Есть ли лучший способ?

4b9b3361

Ответ 1

Проведя время, уточняющее этот вопрос, вариант №1 на самом деле довольно хорош, в последней итерации (функция стрелки, которая передает все реквизиты обратно в пользовательский обработчик). Это позволяет компоненту быть без гражданства и полностью не знать ни одного состояния, которое оно не потребляет. Поэтому я собираюсь назвать это разумным ответом. Я хотел бы услышать ваши лучшие решения!


Определите обработчик отправки, используя функцию стрелки в компоненте формы, и оттуда вызовите мою собственную функцию отправки отправителя, переданную из mapDispatchToProps.

<form onSubmit={ handleSubmit((values)=>{mySubmitHandler(values, this.props.user);}

Затем, чтобы сделать этот обработчик полностью агностиком, передайте все this.props обратно в пользовательский обработчик:

<form onSubmit={ handleSubmit((values)=>{mySubmitHandler(values, this.props);}

Если для функции Submit нужны только значения и реквизиты, которые не были частью формы, мы можем передать только те реквизиты, которые форма не использует. В компоненте без состояния это может выглядеть так:

const Thing_Create = ({ fields: {name, description}, 
    error, 
    handleSubmit, 
    mySubmitHandler, 
    submitting, 
    onCancel, 
    ...otherprops}) => {
return (
<div>
  <form onSubmit={ handleSubmit((values)=>{
    mySubmitHandler(values, otherprops);}) }>
[rest of form definition would go here]

Ответ 2

Лучший способ, который я нашел, очень похож на первое решение, которое вы придумали.

Воспользуйтесь тем, что handleSubmit prop, переданный редукс-формой, может взять функцию, которая будет использоваться в качестве onSubmit prop, и использовать частичное приложение для передачи любых других параметров, которые вам нужны для onSubmit.

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

function updateUser(id, { name, lastname }) { ... }

Предположим, что компонент получает свойство onUpdateUser, которое просто передает параметры прямо к создателю действия. И компонент также получает user, объект с свойством id, который мы хотим передать создателю действия вместе со значениями в полях.

компонент

<form onSubmit={this.props.handleSubmit(this.props.onUpdateUser.bind(null, this.props.user.id))}>

Это может быть легко переписано для функциональных компонентов без состояния, может работать для любого количества параметров, пока вы размещаете их слева в создателе действия, и ему не нужен тяжелый подъем, просто bind

Ответ 3

Мне удалось решить эту проблему, связав this.

Где-то в render()

<MyCustomFormComponent onSubmit={handleSubmit.bind(this)}/>

Элемент handleSumit

  handleSubmit(fields) {
    this.props.onSubmit(fields); //this is properly bound, HURRAY :-)
  }

MapDispatchToProps для хороших разработчиков: -)

const mapDispatchToProps = dispatch => ({
  onSubmit: (fields) =>
    dispatch({type: auth.actionTypes.LOGIN, payload: auth.api.login(fields)})
});

Ответ 4

Это вариант @stoneответа, но вы можете отключить вариант 1 с помощью функционального программирования:

const mySubmitHandler = props => values => { /* ...code here */ }

<form onSubmit={ handleSubmit(mySubmitHandler(this.props)) }>

В этом случае mySubmitHandler вернет функцию, принимающую только один аргумент, который уже закрыт над this.props, поэтому нет необходимости явно упоминать аргумент values, который передает handleSubmit.

Вы можете выполнить каррирование mySubmitHandler более удобным и читаемым способом через рамду.

import { curry } from 'ramda';

const mySubmitHandler = curry((props, values) => { /* ...code here */ });

Вы можете сделать еще один шаг, составив handleSubmit и mySubmitHandler

import { compose, curry } from 'ramda';

const handleSubmit = values => { /* ...code here */ };
const mySubmitHandler = curry((props, values) => { /* ...code here */ });
const onSubmit = compose(handleSubmit, mySubmitHandler);

<form onSubmit={ onSubmit(this.props) }>

Обратите внимание, что onSubmit(this.props) возвращает функцию, которая принимает один аргумент (values) и закрывается над this.props, создавая функцию, которая имеет доступ ко всем необходимым свойствам!