Creazione della propria libreria di convalida delle reazioni: l'esperienza dello sviluppatore (parte 3)
Pubblicato: 2022-03-10Se hai seguito questa piccola serie di articoli, ora hai imparato come mettere insieme la tua libreria di convalida. Può gestire quasi tutte le sfide che puoi lanciargli e aiuta anche con problemi di accessibilità! Il suo unico inconveniente è che fa schifo con cui lavorare.
Sì, è vero. L'esperienza dell'utente dal punto di vista dello sviluppatore è gravemente carente. Non riceviamo alcun avviso utile quando scriviamo in modo errato le parole, utilizziamo in modo improprio le API o, beh, qualsiasi cosa, davvero!
Questo articolo ti guiderà attraverso come puoi migliorare l'esperienza dello sviluppatore della tua libreria di convalida o di qualsiasi libreria per questo.
- Parte 1: Le basi
- Parte 2: Le caratteristiche
- Parte 3: L'esperienza
Inziando
Dall'ultima parte di questo articolo, abbiamo estratto tutto il codice della libreria nei propri file. Dai un'occhiata alla demo di CodeSandbox per vedere con cosa stiamo iniziando.
Funzioni utili
Vogliamo che la nostra libreria sia il più semplice possibile da utilizzare per i casi più comuni. Un modo per raggiungere tale obiettivo è aggiungere utili funzioni di utilità per determinate funzionalità.
Una di queste funzionalità potrebbe essere quella di verificare se il nostro modulo è valido, ovvero se tutti i messaggi di errore sono null
. Questo è qualcosa che in genere controlli nel tuo gestore onSubmit
, ma potrebbe essere utile anche nel tuo metodo di rendering. Mettiamolo in pratica!
const isFormValid = useMemo( () => Object.values(errors).every(error => error === null), [errors] );
Forniremo questo flag nel nostro gestore di moduli onSubmit
, così come nel nostro metodo di rendering.
- Vedi la demo di CodeSandbox
Ce ne sono molti altri che potrebbero essere scritti, ma lascerò che sia un esercizio per il lettore.
Avvisi di sviluppo e invarianti
Una delle migliori caratteristiche di React sono i suoi numerosi avvisi utili della console durante lo sviluppo. Dovremmo fornire lo stesso tipo di qualità anche ai nostri utenti.
Per iniziare, creeremo due funzioni: warning
per la registrazione di avvisi sulla console e invariant
per generare un errore, entrambe se una determinata condizione non viene soddisfatta.
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); }
Vuoi usare invariant
se l'errore va in crash la tua libreria (o la rende inutilizzabile) e warning
per cattive pratiche o altri consigli.
Quando avvisare
Decidere quando avvisare è piuttosto importante. Troppi, e sei solo fastidioso. Troppo pochi e lasci che i bug critici vengano spediti in produzione. Pertanto, dobbiamo essere intelligenti con i nostri avvertimenti.
Poiché la nostra libreria accetta un oggetto di configurazione piuttosto grande, ha senso convalidarlo in qualche modo, almeno durante lo sviluppo. Potremmo risolverlo usando un sistema di tipi come TypeScript o Flow, ma questo esclude tutti i normali utenti JavaScript.
Creiamo invece un controllo dello schema di runtime, in cui convalidiamo che la configurazione contenga i campi corretti e stampiamo gli avvisi pertinenti.
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 }
Probabilmente potremmo continuare a farlo per un po' se volessimo passare il tempo. E dovresti! È un ottimo modo per migliorare l'esperienza degli sviluppatori della tua app.
Tuttavia, non è necessario scriverli a mano. C'è un browser-port della popolare libreria di convalida dello schema di oggetti joi
che potrebbe aiutare a creare un controllo di convalida di runtime davvero carino. Inoltre, come accennato in precedenza, un sistema di tipi aiuterebbe a rilevare gli errori di configurazione in fase di compilazione per gli utenti che utilizzano quel sistema di tipi.
Consenti flessibilità
Una buona esperienza di sviluppo in gran parte non intralcia gli sviluppatori. Diamo un'occhiata ad alcuni modi in cui possiamo migliorare questa esperienza.
Componi oggetti di scena in conflitto
In primo luogo, i nostri prop getter applicano alcuni prop ai nostri input e moduli che possono essere accidentalmente sovrascritti dai nostri consumatori. Invece, aggiungiamo un oggetto prop override ai nostri getter prop, che comporranno insieme eventuali oggetti in conflitto.
Ecco come possiamo implementarlo nel nostro 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] || '', }),
Un approccio simile può essere seguito in getFormProps
.
Aiuta a evitare la perforazione dell'elica
Alcuni moduli potrebbero essere di grandi dimensioni e suddivisi in più componenti. Invece di mettere i puntelli di perforazione dei nostri consumatori giù per l'albero, dovremmo fornire un contesto. In questo modo, possono accedere a tutte le cose che restituiamo dal nostro hook personalizzato ovunque nell'albero sottostante.
Innanzitutto, creiamo un ValidationContext con il metodo createContext
di React:
export const ValidationContext = React.createContext({});
Quindi, creiamo un componente ValidationProvider
, che fornisce invece tutti i valori useValidation
nel contesto:
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} ); };
Ora, invece di chiamare useValidation
direttamente, avvolgiamo il nostro modulo in un componente ValidationProvider
e otteniamo l'accesso ai prop di convalida ( getFormProps
, errors
ecc.) utilizzando l'hook useContext
. Lo useresti in questo modo:
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>} </> ); }
In questo modo otterrai il meglio da entrambi i mondi! Ottieni un semplice hook per quegli scenari semplici e ottieni la flessibilità di cui hai bisogno per quelle parti complesse.
La documentazione è fondamentale
Ogni volta che utilizzo una libreria non l'ho scritta io stesso, adoro la grande documentazione. Ma su cosa dovresti concentrarti e dove dovresti documentare?
Un primo passo dovrebbe essere quello di mettere insieme un README semplice da capire, con gli esempi di utilizzo più basilari prontamente disponibili. Andrew Healey ha scritto un pezzo straordinario su come scrivere un buon README, che consiglio vivamente di leggere.
Dopo aver creato un buon README per far andare le persone, un sito Web di documentazione potrebbe essere una buona idea. Qui puoi inserire una documentazione API più approfondita, ricette per casi d'uso tipici e una buona vecchia FAQ.
Esistono ottimi strumenti per la generazione di siti Web di documentazione. Il mio preferito è docusaurus
di Facebook (umile vanto: lo abbiamo usato durante la creazione del sito Web create-react-app
), ma ci sono diverse buone alternative là fuori.
In questo articolo non esamineremo come scrivere una buona documentazione. Ci sono molti buoni articoli là fuori, persino una community chiamata "Write the Docs". Hanno scritto un'ottima guida su come iniziare a scrivere un'ottima documentazione.
Sommario
Attraverso questa serie di articoli, abbiamo creato una libreria di convalida abbastanza decente. Ha un'API piuttosto semplice, flessibilità per quando ne hai bisogno, una buona esperienza per sviluppatori e molte funzionalità piuttosto scadenti.
Abbiamo esaminato come abbiamo implementato le cose passo dopo passo e spero che tu abbia una comprensione più profonda di come puoi creare la tua libreria e come renderla qualcosa che la gente vorrebbe usare.
Per favore fatemi sapere nei commenti cosa ne pensate e se c'erano alcune parti su cui siete rimasti bloccati o avete avuto difficoltà a capire. Farò del mio meglio per aggiornare l'articolo man mano che il feedback arriva.
Per concludere questo articolo, ecco la versione finale:
- Vedi la demo di CodeSandbox
Grazie per aver letto!