web-dev-qa-db-fra.com

Problème avec les valeurs Formik

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 :

enter image description here

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"));
10
Arthur

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.

Vérifier l'implémentation du formik

3
Seder

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 asyncawait 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.

6
Rikin

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.

  • Je ne recommanderais pas les solutions de contournement (setTimeout, attente aléatoire juste pour contourner le nextTick, etc.) mentionnées dans le problème github ( https://github.com/jaredpalmer/formik/issues/529 ) puisque ils ne sont pas déterministes.
  • Une façon qui a fonctionné pour moi est d'utiliser componentDidUpdate (ou toute autre méthode comparable React Lifecycle)) et d'écouter les changements dans les accessoires.
  • Formik expose les valeurs de champ dans React props du composant à this.props.values
  • Lorsque vous utilisez 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.

2
Royzer William

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);
}}

https://codesandbox.io/s/spring-water-ixuuk

0
Ravi