Réagissez à une méthode alternative de forage de support (inverse, de l'enfant au parent) pour traiter les formulaires
<p>Je suis nouveau sur React et je l'apprends à travers quelques projets pratiques. Je travaille actuellement sur le traitement et la validation des formulaires. J'utilise le composant Form de React Router dans un SPA et, à l'intérieur du formulaire, j'ai un élément FormGroup, qui restitue les entrées d'étiquette et les messages d'erreur. J'utilise également mon propre composant d'entrée au sein du composant FormGroup pour séparer la logique et la gestion de l'état des entrées utilisées dans le formulaire. </p>
<p>J'ai donc placé le composant Form et le composant FormGroup dans l'exemple de page de connexion comme ceci : </p>
<p><em>pages/Login.js</em></p>
<pre class="brush:js;toolbar:false;">import { useState } depuis 'react';
importer {Link, Form, useNavigate, useSubmit } depuis 'react-router-dom' ;
importer FormGroup depuis '../components/UI/FormGroup' ;
importer le bouton depuis '../components/UI/Button' ;
importer la carte depuis '../components/UI/Card' ;
importer './Login.scss';
fonction Page de connexion() {
const naviguer = useNavigate();
const soumettre = useSubmit();
const [isLoginValid, setIsLoginValid] = useState(false);
const [isPasswordValid, setIsPasswordValid] = useState(false);
var réinitialiserLoginInput = null ;
var réinitialiserPasswordInput = null ;
soit isFormValid = false;
if(isLoginValid && isPasswordValid) {
estFormValid = vrai ;
}
fonction formSubmitHandler (événement) {
event.preventDefault();
si(!isFormValid) {
retour;
}
réinitialiserLoginInput();
réinitialiserPasswordInput();
soumettre (événement.currentTarget);
}
fonction loginValidityChangeHandler (isValid) {
setIsLoginValid(isValid);
}
fonction mot de passeValidityChangeHandler(isValid) {
setIsPasswordValid(isValid);
}
fonction resetLoginInputHandler (réinitialisation) {
resetLoginInput = réinitialiser ;
}
fonction resetPasswordInputHandler (réinitialisation) {
réinitialiserPasswordInput = réinitialiser ;
}
fonction switchToSignupHandler() {
naviguer('/inscription');
}
retour (
<div className="connexion">
<div className="login__logo">
Allez la Coupe
</div>
<p className="login__description">
Connectez-vous à votre compte Go Cup
</p>
<Bordure de la carte>
<Form onSubmit={formSubmitHandler}>
<Groupe de formulaires
id="connexion"
label="Nom d'utilisateur ou adresse e-mail"
inputProps={{
tapez : "texte",
nom : "login",
validité : (valeur) => {
valeur = valeur.trim();
si(!valeur) {
return [false, 'Le nom d'utilisateur ou l'adresse e-mail est requis.']
} else if(value.length < 3 || value.length > 30) {
return [false, 'Le nom d'utilisateur ou l'adresse e-mail doit contenir au moins 3 et au maximum 30 caractères'] ;
} autre {
retourner [vrai, nul] ;
}
},
onValidityChange : loginValidityChangeHandler,
onReset : réinitialiserLoginInputHandler
}}
/>
<Groupe de formulaires
id="mot de passe"
label="Mot de passe"
sideLabelElement={
<Lien vers ="/password-reset">
Mot de passe oublié?
</Lien>
}
inputProps={{
tapez : "mot de passe",
nom : "mot de passe",
validité : (valeur) => {
valeur = valeur.trim();
si(!valeur) {
return [false, 'Un mot de passe est requis.']
} else if(value.length < 4 || value.length > 1024) {
return [false, 'Le mot de passe doit comporter au moins 4 ou au maximum 1 024 caractères.'];
} autre {
retourner [vrai, nul] ;
}
},
onValidityChange : mot de passeValidityChangeHandler,
onReset : réinitialiserPasswordInputHandler
}}/>
<div className="text-center">
<Classe du boutonNom="w-100" type="soumettre">
Se connecter
</Bouton>
<span className="login__or">
ou
</envergure>
<Classe du boutonNom="w-100" onClick={switchToSignupHandler}>
S'inscrire
</Bouton>
</div>
</Formulaire>
</Carte>
</div>
);
}
exporter la page de connexion par défaut ;
≪/pré>
<p>Comme vous pouvez le voir dans le code ci-dessus, j'utilise le composant FormGroup et je transmets les propriétés <code>onValidityChange</code> et <code>onReset</code> / La valeur mise à jour du code> Modifications et fonctions de réinitialisation pour réinitialiser la saisie après la soumission du formulaire, etc. Utilisez mon hook personnalisé useInput pour créer les fonctions <code>isValid</code> et <code>reset</code> Je transmets la valeur isValid lorsque la valeur change et je transmets la fonction de réinitialisation du composant d'entrée à l'aide des accessoires définis dans le composant FormGroup. J'utilise également les états <code>isLoginValid</code> et <code>isPasswordValid</code> dans la page de connexion pour stocker la valeur d'état mise à jour <code>isValid</code> composant. J'ai donc défini des états dans le composant d'entrée et les ai transmis au composant parent à l'aide d'accessoires et j'ai stocké leurs valeurs dans d'autres états créés dans ce composant parent. Le forage d’hélices qui se déroulait me mettait un peu mal à l’aise. </p>
<p>L'état est géré à l'intérieur du composant d'entrée, j'ai ces états : </p>
<ul>
<li><strong>Valeur : </strong>Entrez la valeur de l'élément. ≪/li>
<li><strong>isInputTouched</strong> : détermine si l'utilisateur a touché/concentré l'entrée pour déterminer s'il doit afficher un message d'erreur de validation, le cas échéant. ≪/li>
</ul>
<p>Je combine et applique certaines fonctions (telles que la fonction de validation transmise au composant d'entrée) à ces deux états pour créer d'autres valeurs de variable afin de collecter des informations sur l'entrée et sa validité, par exemple si la valeur est valide (isValid ), s'il y a une vérification du message (message), si l'entrée est valide (<code>isInputValid = isValid || !isInputTouched</code>) pour décider d'afficher le message de vérification.</p>
<p>Ces états et valeurs sont gérés dans un hook personnalisé que j'ai créé, <code>useInput</code>, comme ceci : </p>
<p><em>hooks/use-state.js</em></p>
<pre class="brush:js;toolbar:false;">import { useState, useCallback } from 'react';
fonction useInput (validitéFn) {
const [valeur, setValue] = useState('');
const [isInputTouched, setIsInputTouched] = useState(false);
const [isValid, message] = typeof validateFn === 'function' validFn(value) : [true, null];
const isInputValid = isValid || !isInputTouched;
const inputChangeHandler = useCallback(event => {
setValue(event.target.value);
si(!isInputTouched) {
setIsInputTouched(true);
}
}, [isInputTouched]);
const inputBlurHandler = useCallback(() => {
setIsInputTouched(true);
}, []);
const reset = useCallback(() => {
setValeur('');
setIsInputTouched(faux);
}, []);
retour {
valeur,
est valable,
estInputValid,
message,
gestionnaire de changement d'entrée,
gestionnaire de flou d'entrée,
réinitialiser
} ;
}
exporter useInput par défaut ;
≪/pré>
<p>J'utilise actuellement ce hook personnalisé dans Input.js comme ceci : </p>
<p><em>components/UI/Input.js</em></p>
<pre class="brush:js;toolbar:false;">import { useEffect } depuis 'react';
importer useInput depuis '../../hooks/use-input' ;
importer './Input.scss';
fonction Entrée (accessoires) {
const {
valeur,
est valable,
estInputValid,
message,
gestionnaire de changement d'entrée,
gestionnaire de flou d'entrée,
réinitialiser
} = useInput(props.validity);
const {
onIsInputValidOrMessageChange,
surValidityChange,
surRéinitialiser
} = accessoires ;
laissez className = 'form-control';
si(!isInputValid) {
className = `${className} form-control--invalid` ;
}
if(props.className) {
nom de classe = `${nom de classe} ${props.nom de classe}` ;
}
useEffect(() => {
if(onIsInputValidOrMessageChange && typeof onIsInputValidOrMessageChange === 'fonction') {
onIsInputValidOrMessageChange(isInputValid, message);
}
}, [onIsInputValidOrMessageChange, isInputValid, message]);
useEffect(() => {
if(onValidityChange && typeof onValidityChange === 'fonction') {
onValidityChange(isValid);
}
}, [onValidityChange, isValid]);
useEffect(() => {
if(onReset && typeof onReset === 'fonction') {
onReset(réinitialisation);
}
}, [onReset, réinitialiser]);
retour (
<entrée
{...accessoires}
nom de classe={nom de classe}
valeur={valeur}onChange={inputChangeHandler}
onBlur={inputBlurHandler}
/>
);
}
exporter l'entrée par défaut ;
≪/pré>
<p>Dans le composant d'entrée, j'utilise directement l'état <code>isInputValid</code> pour ajouter la classe CSS non valide à l'entrée. Mais je transmets également les fonctions <code>isInputValid</code>, <code>message</code>, <code>isValid</code> à utiliser dedans. Pour transmettre ces états et fonctions, j'utilise les fonctions <code>onIsInputValidOrMessageChange</code>, <code>onValidityChange</code>, <code>onReset</code> enfant à parent). </p>
<p>Voici la définition du composant FormGroup et comment j'utilise l'état d'entrée à l'intérieur du FormGroup pour afficher le message de validation (le cas échéant) : </p>
<p><em>components/UI/FormGroup.js</em></p>
<pre class="brush:js;toolbar:false;">import { useState } depuis 'react';
importer l'entrée depuis './Input' ;
importer './FormGroup.scss';
fonction FormGroup (accessoires) {
const [message, setMessage] = useState(null);
const [isInputValid, setIsInputValid] = useState(false);
let className = 'form-group';
if(props.className) {
className = `form-group ${props.className}` ;
}
laissez labelCmp = (
<label htmlFor={props.id}>
{props.étiquette}
</étiquette>
);
if(props.sideLabelElement) {
étiquetteCmp = (
<div className="form-label-group">
{labelCmp}
{props.sideLabelElement}
</div>
);
}
function isInputValidOrMessageChangeHandler (changedIsInputValid, changesMessage) {
setIsInputValid(changedIsInputValid);
setMessage(changedMessage);
}
retour (
<div className={className}>
{labelCmp}
<Entrée
identifiant={props.id}
onIsInputValidOrMessageChange={isInputValidOrMessageChangeHandler}
{...props.inputProps}
/>
{!isInputValid && <p>{message}</p>}
</div>
);
}
exporter le FormGroup par défaut ;
≪/pré>
<p>Comme vous pouvez le voir dans le code ci-dessus, j'ai défini les états <code>message</code> et <code>isInputValid</code> <code>isInputValid</code> code> L'état transmis par le composant d'entrée. J'ai défini 2 états dans le composant d'entrée pour contenir ces valeurs, mais je dois définir 2 autres états dans ce composant pour stocker les valeurs mises à jour et transmises dans le composant d'entrée. C'est un peu bizarre et cela ne me semble pas être la meilleure solution. </p>
<p><strong>La question est : </strong>Je pense que je peux utiliser React Context (useContext) ou React Redux pour résoudre le problème de forage d'accessoires ici. Mais je ne sais pas si ma gestion actuelle de l'état est mauvaise et peut être améliorée à l'aide de React Context ou React Redux. Parce que d'après ce que j'ai compris, React Context peut être terrible dans les situations où l'état change fréquemment, mais si le Context est utilisé à l'échelle de l'application, alors cela fonctionne. Ici, je peux créer un contexte pour stocker et mettre à jour l'intégralité du formulaire, permettant ainsi une expansion à l'échelle du formulaire. React Redux, en revanche, n'est peut-être pas la meilleure solution pour le silo et peut être un peu exagéré. Qu'en penses-tu? Quelle pourrait être une meilleure alternative dans cette situation particulière ? </p>
<p><strong>Remarque : </strong>Étant donné que je suis nouveau sur React, je suis ouvert à toutes vos suggestions sur tout mon codage, des simples erreurs aux erreurs générales. Merci! </p>