web-dev-qa-db-fra.com

Redux-form handleSubmit: Comment accéder à l'état du magasin?

Dans une fonction redux-form handleSubmit, qui effectue un appel Ajax, certaines propriétés de l'état Redux sont nécessaires dans Ajax (par exemple l'ID utilisateur).

Ce serait assez facile si je voulais définir handleSubmit dans le composant du formulaire: il suffit d'appeler mapStateToProps pour transmettre tout ce dont j'ai besoin et le lire dans this.props dans mon handleSubmit.

Cependant, comme un bon développeur React-Redux, j'écris des composants de vue séparés et des conteneurs , donc mes composants de vue peuvent être simples avec peu ou pas de comportement. Par conséquent, je veux définir handleSubmit dans mon conteneur .

Encore une fois, simple - redux-form est configuré pour cela. Définissez un onSubmit dans mapDispatchToProps, et le formulaire l'appellera automatiquement.

Sauf, oh attendez, dans mapDispatchToProps il n'y a aucun moyen d'accéder à l'état redux.

D'accord, pas de problème, je vais juste passer les accessoires dont j'ai besoin dans handleSubmit avec les valeurs du formulaire.

Hmm, attendez, il est impossible de passer des données dans handleSubmit en utilisant ce mécanisme! Vous obtenez un paramètre, values, et il n'y a aucun moyen d'ajouter un autre paramètre.

Cela laisse les alternatives peu attrayantes suivantes (dont je peux vérifier que 1 et 2 fonctionnent):

1 Define a submit handler in the form component, and from there call my own custom submit handler function passed in from mapDispatchToProps. This is okay, but requires my component to know some props from the redux state that aren't required to display the form. (This issue is not unique to redux-form.)

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

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

Ou, plus concis, tout cela peut être combiné en une fonction de flèche:

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

Ensuite, pour rendre ce gestionnaire complètement agnostique, il pourrait simplement renvoyer l'intégralité de this.props au gestionnaire personnalisé:

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

2 Définissez onSubmit dans mergeProps au lieu de mapDispatchToProps, afin qu'il ait accès à stateProps. Dan Abramov recommande de ne pas utiliser mergeProps (pour des raisons de performances) , cela semble donc sous-optimal.

  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 Copiez les propriétés d'état dans des champs de formulaire redux qui ne sont mappés à aucun contrôle d'entrée dans le composant de formulaire. Cela garantira qu'ils sont accessibles dans le paramètre values renvoyé à handleSubmit. Cela ressemble à une sorte de hack.

Il doit y avoir une meilleure façon!

Existe-t-il un meilleur moyen?

28
stone

Après avoir passé du temps à affiner cette question, l'option # 1 est en fait assez bonne, dans l'itération finale (fonction flèche qui renvoie tous les accessoires au gestionnaire personnalisé). Il permet au composant d'être sans état et complètement ignorant de tout état qu'il ne consomme pas. Je vais donc appeler cela une réponse raisonnable. J'adorerais entendre vos meilleures solutions!


Définissez un gestionnaire de soumission à l'aide d'une fonction de flèche dans le composant de formulaire, et à partir de là, appelez ma propre fonction de gestionnaire de soumission personnalisée transmise depuis mapDispatchToProps.

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

Ensuite, pour rendre ce gestionnaire complètement agnostique, renvoyez l'intégralité de this.props au gestionnaire personnalisé:

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

Si la fonction Soumettre n'a besoin que des valeurs et des accessoires qui ne faisaient pas partie du formulaire, nous pouvons renvoyer uniquement les accessoires que le formulaire n'utilise pas. Dans un composant sans état, cela pourrait ressembler à:

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]
14
stone

La meilleure façon que j'ai trouvée est assez similaire à la première solution que vous avez trouvée.

Profitez du fait que le prop handleSubmit passé par redux-form peut prendre une fonction qui sera utilisée comme prop onSubmit, et utilisez une application partielle pour passer tous les autres paramètres dont vous avez besoin onSubmit.

Créateur d'action:

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

Supposons que le composant obtienne une propriété onUpdateUser qui passe simplement les paramètres directement au créateur de l'action. Et le composant obtient également user, un objet avec la propriété id que nous voulons transmettre au créateur de l'action avec les valeurs dans les champs.

Composant

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

Cela peut facilement être réécrit pour les composants fonctionnels sans état, peut fonctionner pour n'importe quelle quantité de paramètres tant que vous les placez à gauche dans le créateur d'action, et il n'a pas besoin d'être lourd, juste bind

2
Marco Scabbiolo

J'ai pu résoudre ce problème en liant this.

Quelque part dans render ()

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

Le handleSumit

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

MapDispatchToProps pour les bons développeurs: -)

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

Ceci est une variation de @ stone 's answer , mais vous pouvez décomposer l'option 1 via la programmation fonctionnelle:

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

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

Dans ce cas, mySubmitHandler renverra une fonction ne prenant qu'un seul argument déjà fermé sur this.props, Il n'est donc pas nécessaire de mentionner explicitement l'argument values que handleSubmit passe.

Vous pouvez effectuer le currying de mySubmitHandler d'une manière meilleure et plus lisible via ramda .

import { curry } from 'ramda';

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

Vous pouvez aller plus loin en composant handleSubmit et 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) }>

Notez que onSubmit(this.props) retourne une fonction qui prend un argument (values) et a fermé sur this.props, Créant une fonction qui a accès à toutes les propriétés dont elle a besoin!

1
markovchain