web-dev-qa-db-fra.com

J'utilise Redux. Devrais-je gérer l'état d'entrée contrôlé dans le magasin Redux ou utiliser setState au niveau du composant?

J'ai essayé de trouver le meilleur moyen de gérer mes formulaires de réaction. J'ai essayé d'utiliser onChange pour déclencher une action et mettre à jour mon magasin Redux avec mes données de formulaire. J'ai également essayé de créer un état local et lorsque mon formulaire est soumis, je déclenche une action et mets à jour le magasin Redux.

Comment dois-je gérer mon état d'entrée contrôlé?

53
gkkirsch
  1. Vous pouvez utiliser le propre état du composant. Et ensuite, prenez cet état et donnez-le comme argument pour l'action. C'est à peu près la "manière de réagir" décrite dans le documents de réaction .

  2. Vous pouvez également vérifier Redux Form . Il fait fondamentalement ce que vous avez décrit et relie les entrées de formulaire à l’état Redux.

La première méthode implique fondamentalement que vous faites tout à la main - contrôle maximum et passe-partout maximum. La deuxième façon signifie que vous laissez le composant d'ordre supérieur faire tout le travail pour vous. Et puis il y a tout entre les deux. J'ai observé que plusieurs packages simplifient un aspect spécifique de la gestion des formulaires:

  1. React Forms - Il fournit un ensemble de composants auxiliaires pour simplifier le rendu et la validation des formulaires.

  2. React JSON schema - Permet de créer un formulaire HTML à partir d'un schéma JSON.

  3. Formsy React - Comme le dit la description: "Cette extension de React JS se veut un" bon compromis "entre flexibilité et capacité de réutilisation."

Mise à jour: semble ces jours-ci Redux Form est remplacé par:

  1. React Final Form

Et un autre concurrent important dans le domaine à vérifier est:

  1. Formik
29
Dmitry Shvedov

J'aime cette réponse de l'un des co-auteurs de Redux: https://github.com/reactjs/redux/issues/1287

Utilisez React pour un état éphémère qui n'a aucune importance pour l'application et qui ne mute pas de manière complexe. Par exemple, une bascule dans un élément de l'interface utilisateur, un état de saisie du formulaire. Utilisez Redux pour état qui a une importance globale ou qui subit une mutation complexe, par exemple, les utilisateurs en cache ou un post-brouillon.

Parfois vous voudrez passer de l’état Redux à l’état React (lorsque stocker quelque chose dans Redux devient gênant)) ou l’inverse (lorsque davantage de composants doivent avoir accès à un état être local).

La règle de base est la suivante: faites tout ce qui est moins gênant.

En d’autres termes, si vous êtes certain que votre formulaire n’affectera pas l’état global ou n’aura pas besoin d’être conservé après le démontage de votre composant, restez dans l’état de réaction.

39
pgsandstrom

TL; DR

Il n'y a pas de mal à utiliser ce que bon vous semble selon votre application (source: documentation Redux )


Quelques règles courantes pour déterminer le type de données à mettre dans Redux:

  • Est-ce que d'autres parties de l'application s'intéressent à ces données?
  • Avez-vous besoin de pouvoir créer d'autres données dérivées basées sur ces données d'origine?
  • Les mêmes données sont-elles utilisées pour piloter plusieurs composants?
  • Avez-vous intérêt à pouvoir restaurer cet état à un moment donné (par exemple, le débogage de voyage dans le temps)?
  • Voulez-vous mettre les données en cache (c.-à-d., Utilisez ce qui est dans l'état s'il est déjà là au lieu de le redemander)?

Ces questions peuvent facilement vous aider à identifier l'approche qui conviendrait le mieux à votre application. Voici mes points de vue et approches que j'utilise dans mes applications (pour les formulaires):

Etat local

  • Utile lorsque ma forme n'a aucun rapport avec les autres composants de l'interface utilisateur. Capturez simplement les données de input (s) et soumettez-les. Je l'utilise la plupart du temps pour des formulaires simples.
  • Je ne vois pas beaucoup de cas d'utilisation dans le débogage dans le temps du flux d'entrée de mon formulaire (sauf si un autre composant de l'interface utilisateur en dépend).

État de redux

  • Utile lorsque le formulaire doit mettre à jour un autre composant d'interface utilisateur dans mon application (un peu comme liaison bidirectionnelle ).
  • J'utilise ceci lorsque mon formulaire input (s) provoque render d'autres composants en fonction de ce qui est entré par l'utilisateur.
7

Personnellement, je recommande fortement de tout garder dans l’état Redux et de s’éloigner de l’état des composants locaux. Ceci est essentiellement dû au fait que si vous commencez à regarder l'interface utilisateur en fonction de l'état, vous pouvez effectuer des tests complets sans navigateur et profiter de la conservation d'une référence de l'historique complet des états (par exemple, ce qui était dans leurs entrées, quelles boîtes de dialogue étaient ouvertes, etc. , lorsqu’un bogue est touché - pas quel était son état depuis le début du temps - pour l’utilisateur à des fins de débogage. Tweet lié du royaume du clojure

édité pour ajouter: c’est là que notre société sœur et nous évoluons en ce qui concerne nos applications de production et la manière dont nous gérons redux/state/ui

4
Geoffrey Abdallah

L'utilisation des librairies auxiliaires est simplement plus rapide et évite toute la confusion. Ils peuvent être optimisés, riches en fonctionnalités, etc. Comme ils font tous les différents aspects plus d'une brise. Les tester et faire en sorte que votre arsenal sache ce qui est utile et ce qui répond le mieux aux différents besoins est la solution.

Mais si vous avez déjà tout implémenté vous-même . Aller de la manière contrôlée . Et pour une raison , vous avez besoin de redux . Dans l'un de mes projets. Je devais maintenir les états de formulaire. Donc, si je vais à une autre page et reviens, il restera dans le même état. Vous n'avez besoin que de redux, si c'est un moyen de communiquer le changement à plusieurs composants. Ou si c'est un moyen de stocker l'état, vous devez le restaurer.

Vous avez besoin de redux, si l'état doit être global. Sinon vous n'en avez pas besoin. D'ailleurs, vous pouvez consulter ce superbe article ici pour une plongée profonde.

Un des problèmes que vous pouvez rencontrer! Lors de l'utilisation des entrées contrôlées. Est-ce que vous pouvez simplement envoyer les modifications à chaque frappe . Et votre formulaire commencera juste à geler . C'est devenu un escargot.

Vous ne devriez jamais envoyer et utiliser directement le flux de redux, à chaque changement. Ce que vous pouvez faire, c'est que l'état des entrées soit stocké dans l'état local du composant. Et mettez à jour en utilisant setState(). Une fois que l'état change, vous définissez une minuterie avec un délai. Il est annulé à chaque frappe. la dernière frappe est suivie de l'action d'envoi après le délai spécifié. (un délai correct peut être de 500 ms).

(Sachez que setState, gère par défaut les frappes successives multiples de manière efficace. Sinon, nous aurions utilisé la même technique que celle mentionnée ci-dessus (comme dans Vanilla js) [mais nous nous baserons ici sur setState])

Voici un exemple ci-dessous:

onInputsChange(change, evt) {
    const { onInputsChange, roomId } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            ...this.state.inputs,
            ...change
        }
    }, () => {
        // here how you implement the delay timer
        clearTimeout(this.onInputsChangeTimeoutHandler); // we clear at ever keystroke
              // this handler is declared in the constructor
        this.onInputsChangeTimeoutHandler = setTimeout(() => {
           // this will be executed only after the last keystroke (following the delay)
            if (typeof onInputsChange === "function")
                    onInputsChange(this.state.inputs, roomId, evt);
        }, 500);
    })
}

Vous pouvez utiliser l'anti-motif pour initialiser le composant à l'aide des accessoires comme suit:

constructor(props) {
    super(props);

    const {
        name,
        description
    } = this.props;

    this.state = {
        inputs: {
            name,
            description
        }
    }

Dans le constructeur ou dans le hook componentDidMount comme ci-dessous:

componentDidMount () {
    const {
        name, 
        description
    } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            name,
            description
        }
    });
}

Ces dernières nous permettent de restaurer l’état depuis le magasin, à chaque montage de composant.

De même, si vous devez modifier le formulaire à partir d'un composant parent, vous pouvez exposer une fonction à ce parent. En définissant pour setInputs() une méthode liée . Et dans la construction, vous exécutez les accessoires (c'est une méthode de lecture) getSetInputs(). (Un cas utile consiste à réinitialiser les formulaires dans certaines conditions ou dans certains états).

constructor(props) {
    super(props);
    const {
         getSetInputs
    } = this.props;

   // .....
   if (typeof getSetInputs === 'function') getSetInputs(this.setInputs);
}

Pour mieux comprendre ce que j'ai fait ci-dessus, voici comment je mets à jour les entrées:

// inputs change handlers
onNameChange(evt) {
    const { value } = evt.target;

    this.onInputsChange(
        {
            name: value
        },
        evt
    );
}

onDescriptionChange(evt) {
    const { value } = evt.target;

    this.onInputsChange(
        {
            description: value
        },
        evt
    );
}

/**
 * change = {
 *      name: value
 * }
 */
onInputsChange(change, evt) {
    const { onInputsChange, roomId } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            ...this.state.inputs,
            ...change
        }
    }, () => {
        clearTimeout(this.onInputsChangeTimeoutHandler);
        this.onInputsChangeTimeoutHandler = setTimeout(() => {
            if (typeof onInputsChange === "function")
                onInputsChange(change, roomId, evt);
        }, 500);
    })
}

et voici ma forme:

 const {
        name='',
        description=''
 } = this.state.inputs;

// ....

<Form className="form">
    <Row form>
        <Col md={6}>
            <FormGroup>
                <Label>{t("Name")}</Label>
                <Input
                    type="text"
                    value={name}
                    disabled={state === "view"}
                    onChange={this.onNameChange}
                />
                {state !== "view" && (
                    <Fragment>
                        <FormFeedback
                            invalid={
                                errors.name
                                    ? "true"
                                    : "false"
                            }
                        >
                            {errors.name !== true
                                ? errors.name
                                : t(
                                        "You need to enter a no existing name"
                                    )}
                        </FormFeedback>
                        <FormText>
                            {t(
                                "Enter a unique name"
                            )}
                        </FormText>
                    </Fragment>
                )}
            </FormGroup>
        </Col>
        {/* <Col md={6}>
            <div className="image">Image go here (one day)</div>
        </Col> */}
    </Row>

    <FormGroup>
        <Label>{t("Description")}</Label>
        <Input
            type="textarea"
            value={description}
            disabled={state === "view"}
            onChange={this.onDescriptionChange}
        />
        {state !== "view" && (
            <FormFeedback
                invalid={
                    errors.description
                        ? "true"
                        : "false"
                }
            >
                {errors.description}
            </FormFeedback>
        )}
    </FormGroup>
</Form>
0
Mohamed Allal