Fermer

mai 23, 2019

Les caractéristiques (2e partie)


À propos de l'auteur

Kristofer Selbekk est le responsable de React à Bekk et a travaillé sur de nombreux grands projets au cours des 6 dernières années. Il anime une réunion de programmation sur la bière, tente de…
Pour en savoir plus sur Kristofer

Dans son précédent article, Kristofer expliquait comment mettre en œuvre les éléments de base d’une bibliothèque de validation. La prochaine partie portera sur l'amélioration de l'expérience des développeurs, mais l'article d'aujourd'hui portera sur l'ajout de fonctionnalités à ce qui avait été créé dans la Part 1 .

La mise en œuvre d'une bibliothèque de validation n'est pas si difficile. En outre, l'ajout de toutes les fonctionnalités supplémentaires qui rendent votre bibliothèque de validation bien meilleur que les autres.

Cet article continuera à implémenter la bibliothèque de validation que nous avons commencé à implémenter dans la partie précédente de de cette série d'articles.

  • Première partie: Notions fondamentales
  • Deuxième partie: Les caractéristiques
  • Troisième partie: L’expérience ( À venir la semaine prochaine)

Afficher uniquement la validation lors de la soumission

Comme nous validons tous les événements de modification, nous affichons les messages d'erreur utilisateur trop tôt pour une bonne expérience utilisateur. Il existe quelques moyens de remédier à cette situation.

La première solution consiste simplement à fournir le drapeau soumis en tant que propriété restituée du crochet useValidation . De cette façon, nous pouvons vérifier si le formulaire est soumis ou non avant d'afficher un message d'erreur. L'inconvénient est que notre “code d'erreur d'affichage” est un peu plus long:


Une autre approche consiste à fournir un deuxième ensemble d'erreurs (appelons-les soumisErrors ), qui est un objet vide si soumis par est faux et que est erroné ] objet si c'est vrai. Nous pouvons l'implémenter comme ceci:

 const useValidation = config => {
  // comme avant
  revenir {
    erreurs: state.errors,
    subjectErrors: state.submitted? state.errors: {},
  };
}

De cette façon, nous pouvons simplement supprimer le type d'erreur que nous voulons montrer. Bien sûr, nous pourrions également le faire sur le site de l'appel – mais en le fournissant ici, nous le mettons en œuvre une fois au lieu de l'intégrer à tous les consommateurs.

Afficher les messages d'erreur erronés

Beaucoup de gens veulent être montré une erreur une fois qu'ils quittent un certain domaine. Nous pouvons ajouter un support pour cela, en recherchant quels champs ont été "flous" (et en retournant un objet), et en retournant un objet floueErrors similaire au soumisErrors ci-dessus.

Le l'implémentation nécessite de gérer un nouveau type d'action – flou qui mettra à jour un nouvel objet d'état appelé flou :

 const initialState = {
  valeurs: {},
  les erreurs: {},
  floue: {},
  soumis: faux,
};

validation de fonctionRéducteur (état, action) {
  commutateur (action.type) {
    // comme avant
    cas 'flou':
      const floue = {
        ... état.bloui,
        [action.payload]: vrai
      };
      retourne {... état flou};
    défaut:
      jeter une nouvelle erreur ('Type d'action inconnu');
  }
}

Lorsque nous envoyons l'action flou nous créons une nouvelle propriété dans l'objet d'état floue avec le nom du champ comme clé, indiquant que ce champ

La prochaine étape consiste à ajouter un onBlur à notre fonction getFieldProps qui envoie cette action le cas échéant:

 getFieldProps: fieldName => ({
  // comme avant
  onBlur: () => {
    dispatch ({type: 'blur', charge utile: nom du champ});
  },
}),

Enfin, nous devons fournir les objets flous de notre crochet useValidation afin que nous puissions afficher les erreurs uniquement lorsque cela est nécessaire.

 const bluzzErrors = useMemo (() => {
    const returnValue = {};
    for (laissez fieldName dans state.errors) {
      returnValue [fieldName] = state.blowed [fieldName]
        ? state.errors [fieldName]
        : nul;
    }
    return returnValue;
  }, [state.errors, state.blurred]);
revenir {
  // comme avant
  des brouillards,
};

Nous créons ici une fonction mémoisée qui détermine les erreurs à afficher selon que le champ a été flou ou non. Nous recalculons cet ensemble d'erreurs chaque fois que les erreurs ou les objets flous changent. Vous pouvez en savoir plus sur le crochet useMemo dans la documentation .

Le temps d'un petit refactor

Notre composant useValidation renvoie maintenant trois séries de erreurs – dont la plupart auront le même aspect à un moment donné. Au lieu de suivre cette voie, nous allons laisser l'utilisateur spécifier dans la configuration le moment où il souhaite afficher les erreurs dans leur forme.

Notre nouvelle option – showErrors – acceptera soit “ submit »(par défaut),« toujours »ou« flou ». Nous pourrons ajouter d'autres options ultérieurement, si nécessaire.

 function getErrors (state, config) {
  if (config.showErrors === 'toujours') {
    retourne les erreurs;
  }
  if (config.showErrors === 'flou') {
    retourne Object.entries (state.blowed)
      .filter (([, blurred]) => floue)
      .reduce ((acc, [name]) => ({... acc, [name]: state.errors [name]}), {});
  }
  état de retour. Soumis? state.errors: {};
}
const useValidation = config => {
  // comme avant
  erreurs const = useMemo (
    () => getErrors (state, config),
    [state, config]
  )

  revenir {
    les erreurs,
    // comme avant
  };
};

Depuis que le code de traitement des erreurs a commencé à occuper la plus grande partie de notre espace, nous le transformons en une fonction propre. Si vous ne suivez pas les codes Object.entries et .reduce – c'est bien – c'est une réécriture du code pour ... dans dans le dernier

Si nous demandions une validation onBlur ou instantanée, nous pourrions spécifier l’application showError dans notre objet de configuration useValidation .

 const config = {
  // comme avant
  showErrors: 'flou',
};
const {getFormProps, getFieldProps, errors} = useValidation (config);
// les erreurs n'incluent désormais que celles qui ont été floues
Note sur les hypothèses

“Notez que je suppose maintenant que chaque formulaire voudra afficher les erreurs de la même manière (toujours lors de la soumission, toujours dans le flou, etc.). Cela pourrait être vrai pour la plupart des applications, mais probablement pas pour tous. Être conscient de vos hypothèses est un élément important de la création de votre API. "

Autoriser la validation croisée

Une fonctionnalité très puissante d'une bibliothèque de validation consiste à permettre la validation croisée, c'est-à-dire , pour baser la validation d'un champ sur la valeur d'un autre champ.

Pour permettre cela, nous devons faire en sorte que notre hook personnalisé accepte une fonction au lieu d'un objet. Cette fonction sera appelée avec les valeurs de champ actuelles. En réalité, il ne s'agit que de trois lignes de code!

 function useValidation (config) {
  const [state, dispatch] = useReducer (...);
  if (typeof config === 'fonction') {
    config = config (state.values);
  }
}

Pour utiliser cette fonctionnalité, nous pouvons simplement passer une fonction qui renvoie l'objet de configuration à useValidation :

 const {getFieldProps} = useValidation (fields => ({
  mot de passe: {
    isRequired: {message: 'Veuillez renseigner le mot de passe'},
  },
  Répéter le mot de passe: {
    isRequired: {message: 'Veuillez remplir le mot de passe une fois de plus'},
    isEqual: {valeur: fields.password, message: "Vos mots de passe ne correspondent pas"}
  }
}));

Ici, nous utilisons la valeur de fields.password pour nous assurer que deux champs de mot de passe contiennent la même entrée (ce qui est une expérience utilisateur épouvantable, mais ce sera pour un autre article de blog).

Ajouter une certaine accessibilité Victoires

Une bonne chose à faire lorsque vous êtes responsable des accessoires d'un champ est d'ajouter les balises aria correctes par défaut. Cela aidera les lecteurs d'écran à expliquer votre formulaire.

Une amélioration très simple consiste à ajouter aria-invalid = "true" si le champ contient une erreur. Implémentons que:

 const useValidation = config => {
  // comme avant
  revenir {
    // comme avant
    getFieldProps: fieldName => ({
      // comme avant
      'aria-invalid': String (!! erreurs [fieldName]),
    }),
  }
};

C'est un ajouté à une ligne de code et une beaucoup meilleure expérience utilisateur pour les utilisateurs de lecteurs d'écran.

Vous pourriez vous demander pourquoi nous écrivons String (!! state.errors [fieldName]) ? state.errors [fieldName] est une chaîne et l'opérateur de double négation nous donne un booléen (et pas seulement une valeur de vérité ou de falsification). Cependant, la propriété aria-invalid doit être une chaîne (elle peut également lire “grammaire” ou “orthographe”, en plus de “vrai” ou “faux”), nous devons donc forcer ce booléen à son équivalent en chaîne.

Il reste encore quelques modifications à apporter pour améliorer l'accessibilité, mais cela semble être un bon début.

Syntaxe du message de validation abrégée

La plupart des validateurs des calidateurs [] Le paquet (et la plupart des autres validateurs, je suppose) nécessite uniquement un message d'erreur. Ne serait-il pas agréable de pouvoir passer cette chaîne au lieu d'un objet avec une propriété de message contenant cette chaîne?

Implémentons cela dans notre fonction validateField :

 ] fonction validateField (fieldValue = '', fieldConfig, allFieldValues) {
  for (laissez validatorName dans fieldConfig) {
    let validatorConfig = fieldConfig [validatorName];
    if (typeof validatorConfig === ’chaîne ') {
      validatorConfig = {message: validatorConfig};
    }
    const configuréValidator = validateurs [validatorName] (validatorConfig);
    const errorMessage = configureValidator (fieldValue);

    si (message d'erreur) {
      return errorMessage;
    }
  }
  return null;
}

De cette façon, nous pouvons réécrire notre configuration de validation comme suit:

 const config = {
  Nom d'utilisateur: {
    isRequired: 'Le nom d'utilisateur est requis',
    isEmail: 'Le nom d'utilisateur doit être une adresse email valide',
  },
};

Beaucoup plus propre!

Valeurs de champ initiales

Il faut parfois valider un formulaire déjà rempli. Notre hook personnalisé ne le supporte pas encore – alors allons-y!

Les valeurs de champ initiales seront spécifiées dans la configuration pour chaque champ, dans la propriété initialValue . Si ce n'est pas spécifié, sa chaîne par défaut est vide.

Nous allons créer une fonction getInitialState qui créera l'état initial de notre réducteur pour nous.

 function getInitialState (config) ) {
  if (typeof config === 'fonction') {
    config = config ({});
  }
  const initialValues ​​= {};
  const initialBlowed = {};
  for (laissez fieldName dans config.fields) {
    initialValues ​​[fieldName] = config.fields [fieldName] .initialValue || '';
    initialBlurn [fieldName] = false;
  }
  const initialErrors = validateFields (initialValues, config.fields);
  revenir {
    valeurs: initialValues,
    erreurs: initialErrors,
    flou: initialBlowed,
    soumis: faux,
  };
}

Nous examinons tous les champs, vérifions s'ils ont une propriété initialValue et définissons la valeur initiale en conséquence. Nous passons ensuite ces valeurs initiales à travers les validateurs et calculons également les erreurs initiales. Nous renvoyons l'objet d'état initial, qui peut ensuite être transmis à notre crochet useReducer .

Puisque nous introduisons un accessoire non-validateur dans la config des champs, nous devons le sauter lorsque nous validons notre des champs. Pour ce faire, nous modifions notre fonction validateField :

 function validateField (fieldValue = '', fieldConfig) {
  const specialProps = ['initialValue'];
  for (laissez validatorName dans fieldConfig) {
    if (specialProps.includes (validatorName)) {
      continuer;
    }
    // comme avant
  }
}

Tandis que nous continuons à ajouter de nouvelles fonctionnalités comme celle-ci, nous pouvons les ajouter à notre tableau specialProps .

Summing Up

Nous sommes bien partis pour créer une bibliothèque de validation étonnante. Nous avons ajouté des tonnes de fonctionnalités, et nous sommes des leaders assez pensés à ce jour.

Dans la prochaine partie de cette série, nous allons ajouter tous les extras qui font que notre bibliothèque de validation a tendance à être tendance sur LinkedIn. . Restez à l'écoute!

 Éditorial sensationnel (dm, yk, il)




Source link