Creación de su propia biblioteca de validación de React: la experiencia del desarrollador (parte 3)

Publicado: 2022-03-10
Resumen rápido ↬ Ya hemos visto cómo podemos implementar las partes básicas de nuestra biblioteca de validación y cómo agregar todas las funciones que necesitábamos. Esta parte final de esta serie se centrará en mejorar la experiencia del usuario para las personas que utilizarán nuestra biblioteca de validación: los desarrolladores.

Si ha estado siguiendo esta pequeña serie de artículos, ahora ha aprendido cómo armar su propia biblioteca de validación. Puede manejar casi cualquier desafío que pueda lanzarle, ¡e incluso ayuda con los problemas de accesibilidad! Su único inconveniente es que apesta trabajar con él.

Sí, eso es correcto. La experiencia del usuario desde el punto de vista del desarrollador es muy deficiente. No recibimos ninguna advertencia útil cuando escribimos mal las palabras, hacemos mal uso de las API o, bueno, ¡cualquier cosa, en realidad!

Este artículo lo guiará a través de cómo puede mejorar la experiencia del desarrollador de su biblioteca de validación, o cualquier biblioteca por ese motivo.

  • Parte 1: Los fundamentos
  • Parte 2: Las características
  • Parte 3: La Experiencia

Empezando

Desde la última parte de este artículo, hemos extraído todo el código de la biblioteca en sus propios archivos. Eche un vistazo a la demostración de CodeSandbox para ver con qué estamos comenzando.

¡Más después del salto! Continúe leyendo a continuación ↓

Funciones de conveniencia

Queremos que nuestra biblioteca sea lo más simple posible de usar para los casos más comunes. Una forma de avanzar hacia ese objetivo es agregar funciones de utilidad convenientes para ciertas funciones.

Una de esas características podría ser comprobar si nuestro formulario es válido, es decir, si todos los mensajes de error son null . Esto es algo que normalmente verifica en su controlador onSubmit , pero también podría ser útil en su método de renderizado. ¡Vamos a implementarlo!

 const isFormValid = useMemo( () => Object.values(errors).every(error => error === null), [errors] );

Proporcionaremos esta marca en nuestro controlador de formulario onSubmit , así como en nuestro método de representación.

  • Ver demostración de CodeSandbox

Hay muchos más de estos que podrían escribirse, pero dejaré que eso sea un ejercicio para el lector.

Advertencias e invariantes de desarrollo

Una de las mejores características de React son sus muchas advertencias útiles en la consola durante el desarrollo. También deberíamos proporcionar el mismo tipo de calidad a nuestros usuarios.

Para comenzar, crearemos dos funciones: warning para registrar advertencias en la consola e invariant para generar un error, ambas si no se cumple una condición determinada.

 function warning(condition, message) { if (process.env.NODE_ENV === 'production' || condition) { return; } console.warn('useValidation: ' + message); } function invariant(condition, message) { if (process.env.NODE_ENV === 'production' || condition) { return; } throw new Error('useValidation: ' + message); }

Desea usar invariant si el error va a bloquear su biblioteca (o dejarla inútil) y warning por malas prácticas u otros consejos.

Cuándo advertir

Decidir cuándo advertir es muy importante. Demasiados, y eres simplemente molesto. Muy pocos, y dejas que los errores críticos se envíen a producción. Por lo tanto, debemos ser inteligentes con nuestras advertencias.

Dado que nuestra biblioteca acepta un objeto de configuración bastante grande, tiene sentido validarlo de alguna manera, al menos durante el desarrollo. Podríamos resolverlo usando un sistema de tipos como TypeScript o Flow, pero eso excluye a todos los usuarios habituales de JavaScript.

En su lugar, creemos un verificador de esquemas en tiempo de ejecución, donde validemos que la configuración contenga los campos correctos e imprimamos advertencias relevantes.

 function validateConfigSchema(config) { if (process.env.NODE_ENV === 'production') { return; } if (typeof config === 'function') { config = config({}); } invariant( typeof config === 'object', `useValidation should be called with an object or a function returning an object. You passed a ${typeof config}.`, ); invariant( typeof config.fields === 'object', 'useValidation requires a `field` prop with an object containing the fields and their validators. Please refer to the documentation on usage: https://link.to/docs' ); invariant( Object.values(config.fields).every(field => typeof field === 'object'), 'useValidation requires that the `field` object only contains objects. It looks like yours isn\'t. Please refer to the documentation on usage: https://link.to/docs' ); warning( ['always', 'blur', 'submit', undefined].includes(config.showError), 'useValidation received an unsupported value in the `showError` prop. Valid values are "always", "blur" or "submit".' ) // And so on }

Probablemente podríamos seguir haciendo esto por un tiempo si quisiéramos pasar el tiempo. ¡Y deberías! Es una excelente manera de mejorar la experiencia del desarrollador de su aplicación.

Sin embargo, no es necesario que los escribas a mano. Hay un puerto de navegador de la popular biblioteca de validación de esquemas de objetos joi que podría ayudar a crear una verificación de validación de tiempo de ejecución realmente agradable. Además, como se mencionó anteriormente, un sistema de tipos ayudaría a detectar errores de configuración en tiempo de compilación para los usuarios que usan ese sistema de tipos.

Permitir flexibilidad

Una buena experiencia de desarrollador consiste en gran parte en no interponerse en el camino de los desarrolladores. Veamos algunas maneras en que podemos mejorar esa experiencia.

Componer accesorios en conflicto

Primero, nuestros captadores de accesorios aplican algunos accesorios a nuestras entradas y formularios que nuestros consumidores pueden anular accidentalmente. En su lugar, agreguemos un objeto de anulación de accesorios a nuestros captadores de accesorios, que compondrán todos los accesorios en conflicto.

Así es como podemos implementar esto en nuestro getFieldProps :

 getFieldProps: (fieldName, overrides = {}) => ({ onChange: e => { const { value } = e.target; if (!config.fields[fieldName]) { return; } dispatch({ type: 'change', payload: { [fieldName]: value }, }); if (overrides.onChange) { overrides.onChange(e); } }, onBlur: e => { dispatch({ type: 'blur', payload: fieldName }); if (overrides.onBlur) { overrides.onBlur(e) } }, name: overrides.name || fieldName, value: state.values[fieldName] || '', }),

Se puede seguir un enfoque similar en getFormProps .

Ayude a evitar la perforación de apoyo

Algunos formularios pueden ser grandes y estar divididos en varios componentes. En lugar de convertir los apoyos de perforación de nuestros consumidores en el árbol, debemos proporcionar un contexto. De esta manera, pueden acceder a todas las cosas que devolvemos desde nuestro enlace personalizado en cualquier parte del árbol a continuación.

Primero, creemos un ValidationContext con el método createContext de React:

 export const ValidationContext = React.createContext({});

A continuación, creemos un componente ValidationProvider , que proporcione todos los valores del useValidation en contexto:

 export const ValidationProvider = props => { const context = useValidation(props.config); return ( {props.children} ); }; export const ValidationProvider = props => { const context = useValidation(props.config); return ( {props.children} ); }; export const ValidationProvider = props => { const context = useValidation(props.config); return ( {props.children} ); };

Ahora, en lugar de llamar a useValidation directamente, envolveríamos nuestro formulario en un componente ValidationProvider y obtendríamos acceso a los accesorios de validación ( getFormProps , errors , etc.) mediante el uso del useContext . Lo usarías así:

 Import React, { useContext } from 'react'; import { ValidationContext } from './useValidation'; function UsernameForm(props) { const { getFieldProps, errors } = useContext(ValidationContext); return ( <> <input {...getFieldProps('username')} /> {errors.username && {errors.username}></span>} </> ); }

¡De esta manera, obtienes lo mejor de ambos mundos! Obtiene un gancho simple para esos escenarios simples y obtiene la flexibilidad que necesita para esas partes complejas.

La documentación es clave

Cada vez que uso una biblioteca que no escribí yo mismo, me encanta la excelente documentación. Pero, ¿en qué debería centrarse y dónde debería documentar?

Un primer paso debe ser armar un LÉAME fácil de entender, con los ejemplos de uso más básicos disponibles. Andrew Healey escribió un artículo increíble sobre cómo escribir un buen LÉAME, que recomiendo encarecidamente que lea.

Cuando haya creado un buen LÉAME para animar a la gente, un sitio web de documentación podría ser una buena idea. Aquí, puede poner una documentación API más detallada, recetas para casos de uso típicos y una buena pregunta frecuente.

Existen excelentes herramientas para generar sitios web de documentación. Mi favorito es docusaurus de Facebook (humilde jactancia: lo usamos cuando creamos el sitio web create-react-app ), pero hay varias buenas alternativas por ahí.

No vamos a explicar cómo escribir una buena documentación en este artículo. Hay varios buenos artículos por ahí, incluso una comunidad llamada "Escribe los documentos". Han escrito una excelente guía sobre cómo puede comenzar a escribir una excelente documentación.

Resumen

A través de esta serie de artículos, hemos creado una biblioteca de validación bastante decente. Tiene una API bastante simple, flexibilidad para cuando la necesites, una buena experiencia de desarrollador y muchas características geniales.

Hemos repasado cómo implementamos las cosas paso a paso, y espero que tenga una comprensión más profunda de cómo puede crear su propia biblioteca y cómo hacer que sea algo que a la gente le encantaría usar.

Déjame saber en los comentarios lo que piensas, y si hubo algunas partes en las que te atascaste o te costó entender. Haré todo lo posible para actualizar el artículo a medida que lleguen los comentarios.

Para terminar este artículo, aquí está la versión final:

  • Ver demostración de CodeSandbox

¡Gracias por leer!