Salut
Nous avons un panneau d'administration et de nombreux formulaires dans le BCS, mais dans la communauté React il n'y a pas de méthode généralement acceptée - comment les concevoir pour les réutiliser. Le guide Facebook officiel ne dispose pas d'informations détaillées sur la façon de travailler avec des formulaires en conditions réelles, où la validation et la réutilisation sont nécessaires. Quelqu'un utilise redux-form, formik, final-form, ou même écrit sa propre solution.
Dans cet article, nous allons montrer l'une des options pour travailler avec des formulaires sur React. Notre pile sera comme ceci: React + formik + Typescript. Nous montrerons:
- Ce qu'un composant doit faire.
- Config, champs et validation au niveau des accessoires.
- Comment rendre un formulaire réutilisable.
- Optimisation du rendu.
- Que notre méthode n'est pas pratique.
Avec la nouvelle tâche commerciale, nous avons appris que nous devrons créer 15 à 20 formulaires similaires, et il pourrait y en avoir encore plus. Nous avions une forme de dinosaure dans la config, qui fonctionnait avec les données du «magasin», envoyait des actions pour enregistrer et exécuter les demandes via «sagas». Elle était merveilleuse, faisant de la valeur commerciale. Mais il était déjà non extensible et non réutilisable, seulement avec un mauvais code et l'ajout de béquilles.
La tâche est la suivante: réécrire le formulaire afin qu'il puisse être réutilisé un nombre illimité de fois. Eh bien, rappelez-vous la programmation fonctionnelle, elle a des fonctions pures qui n'utilisent pas de données externes, dans notre cas «redux», seulement ce qu'elles sont envoyées en arguments (accessoires).
Et c'est ce qui s'est passé.
L'idée de notre composant est de créer un wrapper (conteneur) et d'y écrire la logique de travailler avec le monde extérieur (recevoir des données du magasin Redux et envoyer des actions). Pour cela, le composant conteneur doit pouvoir recevoir des informations via les rappels. La liste complète des accessoires de formulaire:
interface IFormProps {
Utilisation de Formik
Nous utilisons le composant <Formik />.
render() { const { fields, validationSchema, validateOnBlur = true, validateOnChange = true, } = this.props; return ( <Formik initialValues={fields} render={this.renderForm} onSubmit={this.handleSubmitForm} validationSchema={validationSchema} validateOnBlur={validateOnBlur} validateOnChange={validateOnChange} validate={this.validateFormLevel} /> ); }
Dans le cadre du formulaire `validate`, nous appelons la méthode` this.validateFormLevel`, dans laquelle nous donnons au composant conteneur la possibilité d'obtenir tous les champs modifiés et de vérifier s'ils sont valides.
private validateFormLevel = (values: FormFields) => { const { onChangeFields, validationSchema } = this.props; if (onChangeFields) { validationSchema .validate(values) .then(() => { onChangeFields(values, { isValid: true }); }) .catch(() => { onChangeFields(values, { isValid: false }); }); } }
Ici, nous devons à nouveau appeler la validation afin d'indiquer clairement au conteneur si les champs sont valides. Lors de la soumission d'un formulaire, nous appelons simplement prop `onSubmit`:
private handleSubmitForm = (): void => { const { onSubmit } = this.props; if (onSubmit) { onSubmit(); } }
Avec les accessoires 1 à 5, tout devrait être clair. Passons maintenant à «config», «fields» et «validationSchema».
Configuration des accessoires
interface IFieldsFormMetaModel { sectionName?: string; sectionDescription?: string; fieldsForm?: Array<{ name?: string;
Sur la base de cette interface, nous créons un tableau d'objets et rendons «section» -> «champs de section» selon ce schéma. Nous pouvons donc afficher plusieurs champs pour la section ou dans chacun à la fois, si vous avez besoin d'un titre et d'une note. Comment le rendu fonctionne, nous le montrerons un peu plus tard.
Un petit exemple de configuration:
export const config: IFieldsFormMetaModel[] = [ { sectionName: ' ', fieldsForm: [{ name: 'subject', label: '', type: ElementTypes.Text, }], }, { sectionName: '', sectionDescription: ' ', fieldsForm: [{ name: 'reminder', disabled: true, label: '', type: ElementTypes.CheckBox, checked: true, }], }, ];
Sur la base des données d'entreprise, les valeurs des clés `nom` sont définies. Les mêmes valeurs sont utilisées dans les clés prop `fields` pour transmettre les valeurs originales ou modifiées pour le formic.
Pour l'exemple ci-dessus, `champs` pourrait ressembler à ceci:
const fields: SomeBusinessApiFields = { subject: ' ', reminder: 'yes', }
Pour la validation, nous devons passer le schéma Yup. Nous donnons le formulaire au formulaire avec les accessoires de conteneur, décrivant là les interactions avec les données externes, par exemple, les demandes.
Le formulaire ne peut en aucun cas influencer le schéma, par exemple:
export const CreateClientSchema: ( props: CreateClientProps, ) => Yup.MixedSchema = (props: CreateClientProps) => Yup.object( { subject: Yup.string(), description: Yup.string(), date: dateSchema, address: addressSchema(props), }, );
Optimisation du rendu et des champs
Pour le rendu, nous avons fait une carte pour une recherche rapide par clé. Il semble concis et la recherche est plus rapide que par `switch`.
fieldsMap: Record< ElementTypes, ( state: FormikFieldState, handlers: FormikHandlersState, field: IFieldsFormInfo, ) => JSX.Element > = { [ElementTypes.Text]: ( state: FormikFieldState, handlers: FormikHandlersState, field: IFieldsFormInfo ) => { const { values, errors, touched } = state; return ( <FormTextField key={field.name} element={field} handleChange={this.handleChangeField(handlers.setFieldValue, field.name)} handleBlur={handlers.handleBlur} value={values[field.name]} error={touched[field.name] && errors[field.name] || ''} /> ); }, [ElementTypes.TextSearch]: (...) => {...}, [ElementTypes.TextArea]: (...) => {...}, [ElementTypes.Date]: (...) => {...}, [ElementTypes.CheckBox]: (...) => {...}, [ElementTypes.RadioButton]: (...) => {...}, [ElementTypes.Select]: (...) => {...}, };
Chaque champ de composant est avec état. Il se trouve dans un fichier séparé et est enveloppé dans `React.memo`. Toutes les valeurs sont transmises via des accessoires, en contournant les `enfants`, pour éviter un rendu inutile.
Conclusion
Notre formulaire n'est pas idéal, pour chaque cas, nous devons créer un emballage de conteneur pour travailler avec les données. Enregistrez-les dans le `store`, convertissez et faites des demandes. Il y a une répétition de code dont vous voulez vous débarrasser. Nous essayons de trouver une nouvelle solution dans laquelle le formulaire, selon les accessoires, prendra la clé souhaitée du magasin avec des champs, des actions, des diagrammes et une configuration. Dans l'un des articles suivants, nous vous dirons ce qui en est sorti.