Discussion GitHub sur ce problème - https://github.com/jaredpalmer/formik/issues/529
J'ai passé des valeurs formik à une fonction qui est appelée dans onChange de <Field/>
Mais quand je tape dans <Field/>
, Le dernier caractère ne tombe pas dans la fonction.
CodeSandbox - lien
Capture d'écran :
Instance de code minimale :
import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field, FieldArray } from "formik";
function App() {
//------ HERE ----------
const getValues = values => console.log(values.fields[0]);
//----------------------
return (
<>
<Formik
initialValues={{ fields: [""] }}
onSubmit={(values, actions) => {
actions.setSubmitting(false);
console.log(values);
return false;
}}
render={({ setFieldValue, values }) => (
<Form>
<FieldArray
name="fields"
render={() => (
<Field
type="text"
name="fields.0"
placeholder="Write something"
onChange={e => {
setFieldValue("fields.0", e.target.value);
//---------------
getValues(values);
//---------------
}}
/>
)}
/>
</Form>
)}
/>
</>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
Je ne connais pas formik et je viens de jeter un coup d'œil sur la mise en œuvre. Mais voici mes deux cents sur la question.
Pourquoi les valeurs ne sont-elles pas mises à jour après chaque appel?
Vous essayez d'obtenir les valeurs avant que le rendu ne se produise et la valeur que vous avez est toujours l'ancienne, car le composant n'est pas encore mis à jour.
Pour vérifier, essayez d'ajouter console.log(values.fields[0]);
dans le composant App avant la définition getValue.
Pourquoi l'appel se produit-il avant le rendu?
L'implémentation onChange
déclenchera le handleChange
dans lequel il s'agit d'un appel de fonction mémorisé qui ne sera pas déclenché si vous avez la même valeur MAIS vous avez modifié la valeur.
handleChange
sera déclenché si la valeur executeChange
est différente et executeChange
également mémorisée en fonction de setFieldValue
ou du state.values
.
setFieldValue
est un rappel d'événement qui ne met à jour la référence qu'après chaque rendu. C'est à partir de documents formik // we copy a ref to the callback scoped to the current state/props on each render
et cela ne s'est pas encore produit dans votre cas -useEventCallback
-.
Une fois que l'état est mis à jour par l'action, votre composant sera mis à jour mais votre fonction n'est pas encore appelée.
setFieldValue
essaiera de valider l'entrée et renverra une promesse et une bulle jusqu'à executeChange
et handleChange
et il la traitera comme une faible priorité, donc ce n'est pas un appel bloquant.
Une solution rapide pourrait être d'utiliser onKeyUp
au lieu de onChange
Je pense que cela contournera le useCallback
et mettra à jour le composant avec appel prioritaire
function App({ values, setFieldValue }) {
console.log("Rerendering",values.fields[0])
//------ HERE ----------
const getValues = () => console.log(values.fields[0]);
//----------------------
return (
<div className="formik-wrapper">
<Form>
<FieldArray
name="fields"
render={() => (
<Field
type="text"
name="fields.0"
placeholder="Write something"
onKeyUp={e => {
setFieldValue("fields.0", e.target.value);
getValues();
}}
/>
)}
/>
</Form>
</div>
);
}
J'espère que cela peut aider ou au moins conduire à de l'aide.
Cette réponse est basée sur Formik v1.5.7
Vous essayez d'obtenir la valeur périmée de values
dans votre code juste après avoir appelé setFieldValue
qui effectue une opération asynchrone en interne, vous ne pouvez donc pas vous attendre à ce que les valeurs soient modifiées juste après son retour (appel). Ainsi, lorsque vous essayez de déconnecter la valeur de values
, vous obtenez un objet d'état pendant le cycle de rendu en cours (en cours).
Lorsque values
change, Formik restitue le formulaire dans lequel il aura la valeur souhaitée que vous essayez d'obtenir.
J'ai remonté le code dans votre exemple pour expliquer cela.
Voici le lien de code mis à jour .
Étant donné que cette question est mise à jour après ma réponse, je commenterai ce sujet problème GitHub . Le problème est lié à la demande de développement d'amélioration et les solutions dans les commentaires ne fonctionneront pas dans votre exemple car values
sera toujours des valeurs "OLD".
Voici la raison pour laquelle la solution async
await
ne fonctionnera pas dans ce cas:
onChange={async e => {
await setFieldValue("fields.0", e.target.value);
getValues(values);
}}
Au-dessus du code lors de l'événement click awaits
lors de l'appel de fonction setFieldValue
qui est exécuté et définit en interne l'état qui est une opération asynchrone placée dans la pile d'exécution. Retour d'appel et console.log
journaux values
qui se trouve être le cycle de rendu existant values
.
J'espère que cela clarifie.
Si je comprends bien votre question, vous avez besoin d'un moyen fiable pour obtenir le dernier état des valeurs Formik, juste après avoir défini la valeur d'un champ et déclencher une autre méthode/fonction/événement basé sur ce `` nouveau '' ensemble de valeurs. L'obstacle évident ici est que setFieldValue()
est une méthode asynchrone qui utilise en interne setState()
et donc il n'y a aucun moyen direct pour y parvenir d'une manière que vous avez décrite/attendue.
componentDidUpdate
(ou toute autre méthode comparable React Lifecycle)) et d'écouter les changements dans les accessoires.this.props.values
componentDidUpdate
, il vous suffit de comparer les accessoires précédents aux nouveaux accessoires pour vérifier si quelque chose a changé.Voici un code minimal pour montrer ce que je veux dire.
const _ = require('lodash');
class Sample extends React.Component {
componentDidUpdate(prevProps) {
if(!_.isEqual(prevProps.values, this.props.values)) {
// At least one of the Formik fields have changed. And this.props.values holds this newest data about of the fields.
triggerWhateverWithNewValues(this.props.values);
}
}
}
J'espère que cela t'aides.
Lorsque vous appelez getValues
, elle obtient l'ancienne valeur sous la forme setFieldValue
L'appel de fonction n'est pas terminé (la valeur n'est donc pas définie sur Field.0
). vous pouvez cependant passer directement la valeur à la fonction getValues
.
const getValues = valueField => console.log(valueField);
onChange={e => {
setFieldValue("fields.0", e.target.value);
getValues(e.target.value);
}}