Criando sua própria biblioteca de validação do React: a experiência do desenvolvedor (parte 3)
Publicados: 2022-03-10Se você tem acompanhado esta pequena série de artigos, agora aprendeu como montar sua própria biblioteca de validação. Ele pode lidar com quase qualquer desafio que você possa lançar e até ajuda com questões de acessibilidade! Sua única queda é que é uma droga trabalhar com ele.
Sim, isso mesmo. A experiência do usuário do ponto de vista do desenvolvedor está seriamente ausente. Não recebemos nenhum aviso útil quando digitamos palavras incorretamente, fazemos uso indevido de APIs ou, bem, qualquer coisa, realmente!
Este artigo irá orientá-lo sobre como você pode melhorar a experiência do desenvolvedor de sua biblioteca de validação — ou qualquer biblioteca para esse fim.
- Parte 1: O básico
- Parte 2: Os Recursos
- Parte 3: A Experiência
Começando
Desde a última parte deste artigo, colocamos todo o código da biblioteca em seus próprios arquivos. Dê uma olhada na demonstração do CodeSandbox para ver com o que estamos começando.
Funções de conveniência
Queremos que nossa biblioteca seja o mais simples possível de usar para os casos mais comuns. Uma maneira de atingir esse objetivo é adicionar funções utilitárias convenientes para determinadas funcionalidades.
Um desses recursos pode ser verificar se nosso formulário é válido — ou seja, se todas as mensagens de erro são null
. Isso é algo que você normalmente verifica em seu manipulador onSubmit
, mas também pode ser útil em seu método de renderização. Vamos implementá-lo!
const isFormValid = useMemo( () => Object.values(errors).every(error => error === null), [errors] );
Forneceremos esse sinalizador em nosso manipulador de formulário onSubmit
, bem como em nosso método de renderização.
- Veja a demonstração do CodeSandbox
Há muito mais desses que poderiam ser escritos, mas vou deixar isso ser um exercício para o leitor.
Avisos e invariantes de desenvolvimento
Um dos maiores recursos do React são seus muitos avisos úteis do console durante o desenvolvimento. Devemos fornecer o mesmo tipo de qualidade para nossos usuários também.
Para começar, criaremos duas funções — warning
para registrar avisos no console e invariant
para gerar um erro — ambas se uma determinada condição não for atendida.
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); }
Você deseja usar invariant
se o erro for travar sua biblioteca (ou torná-la inútil) e warning
sobre práticas ruins ou outros conselhos.
Quando avisar
Decidir quando avisar é muito importante. Muitos, e você é apenas irritante. Muito poucos, e você permite que bugs críticos sejam enviados para produção. Portanto, precisamos ser inteligentes com nossos avisos.
Como nossa biblioteca aceita um objeto de configuração bem grande, faz sentido validar isso de alguma forma — pelo menos durante o desenvolvimento. Poderíamos resolvê-lo usando um sistema de tipos como TypeScript ou Flow, mas isso exclui todos os usuários regulares de JavaScript.
Em vez disso, vamos criar um verificador de esquema de tempo de execução, onde validamos se a configuração contém os campos corretos e imprimimos avisos 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 }
Provavelmente poderíamos continuar fazendo isso por um tempo se quiséssemos passar o tempo. E você deveria! É uma ótima maneira de melhorar a experiência do desenvolvedor do seu aplicativo.
Você não tem que estar escrevendo estes à mão, no entanto. Há uma porta de navegador da popular biblioteca de validação de esquema de objeto joi
que pode ajudar na criação de uma verificação de validação de tempo de execução muito boa. Além disso, como mencionado anteriormente, um sistema de tipos ajudaria a detectar erros de configuração em tempo de compilação para os usuários que usam esse sistema de tipos.
Permitir flexibilidade
Uma boa experiência de desenvolvedor não está em grande parte atrapalhando os desenvolvedores. Vejamos algumas maneiras de melhorar essa experiência.
Compor adereços conflitantes
Primeiro, nossos prop getters aplicam alguns adereços às nossas entradas e formulários que podem ser substituídos acidentalmente por nossos consumidores. Em vez disso, vamos adicionar um objeto de substituição de prop aos nossos getters de prop, que comporão quaisquer props conflitantes juntos.
Veja como podemos implementar isso em nosso 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] || '', }),
Uma abordagem semelhante pode ser seguida em getFormProps
.
Ajude a evitar a perfuração de hélices
Alguns formulários podem ser grandes e divididos em vários componentes. Em vez de colocar os adereços de perfuração de nossos consumidores na árvore, devemos fornecer um contexto. Dessa forma, eles podem acessar todas as coisas que retornamos do nosso gancho personalizado em qualquer lugar da árvore abaixo.
Primeiro, vamos criar um ValidationContext com o método createContext
do React:
export const ValidationContext = React.createContext({});
Em seguida, vamos criar um componente ValidationProvider
, que fornece todos os valores do gancho useValidation
no 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} ); };
Agora, em vez de chamar useValidation
diretamente, nós envolveríamos nosso formulário em um componente ValidationProvider
e useContext
acesso às props de validação ( getFormProps
, errors
etc.) Você usaria assim:
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>} </> ); }
Dessa forma, você obtém o melhor dos dois mundos! Você obtém um gancho simples para esses cenários simples e obtém a flexibilidade necessária para essas partes complexas.
Documentação é a chave
Sempre que estou usando uma biblioteca que não escrevi, adoro uma ótima documentação. Mas em que você deve se concentrar e onde deve documentar?
Um primeiro passo deve ser montar um README simples de entender, com os exemplos de uso mais básicos prontamente disponíveis. Andrew Healey escreveu um artigo incrível sobre como escrever um bom README, que eu recomendo que você leia.
Quando você cria um bom README para motivar as pessoas, um site de documentação pode ser uma boa ideia. Aqui, você pode colocar uma documentação de API mais detalhada, receitas para casos de uso típicos e um bom e velho FAQ.
Existem ótimas ferramentas para gerar sites de documentação. Meu favorito é docusaurus
do Facebook (humilde me gabar: nós o usamos ao criar o site create-react-app
), mas existem várias alternativas boas por aí.
Não vamos falar sobre como escrever uma boa documentação neste artigo. Existem vários artigos bons por aí – até mesmo uma comunidade chamada “Write the Docs”. Eles escreveram um ótimo guia sobre como você pode começar a escrever uma ótima documentação.
Resumo
Por meio desta série de artigos, criamos uma biblioteca de validação bastante decente. Ele tem uma API bastante simples, flexibilidade para quando você precisar, uma boa experiência de desenvolvedor e muitos recursos bastante obscuros.
Examinamos como implementamos as coisas passo a passo e espero que você tenha uma compreensão mais profunda de como criar sua própria biblioteca e como torná-la algo que as pessoas adorariam usar.
Por favor, deixe-me saber nos comentários o que você pensa, e se houve algumas partes em que você ficou preso ou teve dificuldade em entender. Vou tentar o meu melhor para atualizar o artigo à medida que o feedback for chegando.
Para terminar este artigo - aqui está a versão final:
- Veja a demonstração do CodeSandbox
Obrigado por ler!