創建您自己的 React 驗證庫:開發者體驗(第 3 部分)
已發表: 2022-03-10如果您一直在關注這個小文章系列,那麼您現在已經學會瞭如何組合您自己的驗證庫。 它幾乎可以處理您可以提出的任何挑戰,甚至可以幫助解決可訪問性問題! 它唯一的缺點是使用起來很糟糕。
是的,沒錯。 從開發人員的角度來看,用戶體驗嚴重不足。 當我們拼錯單詞、濫用 API 或其他任何事情時,我們不會收到任何有用的警告!
本文將指導您了解如何改善您的驗證庫的開發人員體驗——或任何為此目的的庫。
- 第 1 部分:基礎知識
- 第 2 部分:功能
- 第 3 部分:體驗
開始
從本文的最後一部分開始,我們已將所有庫代碼提取到其自己的文件中。 看看 CodeSandbox 演示,看看我們從什麼開始。
便利功能
我們希望我們的庫盡可能簡單地用於最常見的情況。 實現該目標的一種方法是為某些功能添加方便的實用程序功能。
一個這樣的功能可能是檢查我們的表單是否有效——也就是說,如果所有錯誤消息都是null
。 這是您通常在onSubmit
處理程序中檢查的內容,但它在您的渲染方法中也可能很有用。 讓我們實現它!
const isFormValid = useMemo( () => Object.values(errors).every(error => error === null), [errors] );
我們將在我們的onSubmit
表單處理程序以及我們的渲染方法中提供這個標誌。
- 請參閱 CodeSandbox 演示
還有很多這些可以寫,但我會讓它成為讀者的練習。
開發警告和不變量
React 最大的特性之一是它在開發過程中提供了許多有用的控制台警告。 我們也應該為我們的用戶提供同樣的質量。
首先,我們將創建兩個函數——用於將warning
記錄到控制台的warning,以及用於拋出錯誤的invariant
如果不滿足給定條件的話。
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); }
如果錯誤會使您的庫崩潰(或使其無用),您希望使用invariant
,並warning
不良做法或其他建議。
何時發出警告
決定何時發出警告非常重要。 太多了,你只是煩人。 太少了,你會讓關鍵的錯誤交付到生產環境中。 因此,我們需要明智地處理警告。
由於我們的庫接受一個相當大的配置對象,因此以某種方式驗證它是有意義的——至少在開發時是這樣。 我們可以使用 TypeScript 或 Flow 之類的類型系統來解決它,但這會排除所有常規的 JavaScript 用戶。
相反,讓我們創建一個運行時模式檢查器,我們在其中驗證配置是否包含正確的字段,並打印相關警告。
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 }
如果我們想花時間,我們可能會繼續這樣做一段時間。 你應該! 這是改善應用程序開發人員體驗的好方法。
但是,您不必手動編寫這些內容。 流行的對像模式驗證庫joi
有一個瀏覽器端口,可以幫助創建一個非常好的運行時驗證檢查。 此外,如前所述,類型系統將有助於在編譯時為使用該類型系統的用戶捕獲配置錯誤。
允許靈活性
良好的開發人員體驗在很大程度上不會妨礙開發人員。 讓我們看看可以改善這種體驗的幾種方法。
編寫衝突的道具
首先,我們的 prop getter 將一些 props 應用於我們的輸入和表單,這些 props 可能會被我們的消費者意外覆蓋。 相反,讓我們為我們的 prop getter 添加一個 prop override 對象,它將所有衝突的 prop 組合在一起。
下面是我們如何在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] || '', }),
在getFormProps
中可以採用類似的方法。
幫助避免螺旋槳鑽孔
一些表格可能很大並且分成幾個部分。 我們應該提供一個上下文,而不是讓我們的消費者鑽到樹上。 這樣,他們可以訪問我們從下面樹中任何地方的自定義鉤子返回的所有內容。
首先,讓我們使用 React 的createContext
方法創建一個 ValidationContext:
export const ValidationContext = React.createContext({});
接下來,讓我們創建一個組件ValidationProvider
,它在上下文中提供來自useValidation
鉤子的所有值:
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} ); };
現在,我們不是直接調用useValidation
,而是將表單包裝在ValidationProvider
組件中,並通過使用useContext
掛鉤訪問驗證道具( getFormProps
、 errors
等)。 你會這樣使用它:
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>} </> ); }
這樣,您將獲得兩全其美! 對於那些簡單的場景,您可以獲得一個簡單的鉤子,並且您可以獲得那些複雜部分所需的靈活性。
文檔是關鍵
每當我使用不是我自己編寫的庫時,我都喜歡出色的文檔。 但是你應該關注什麼,你應該在哪裡記錄?
第一步應該是編寫一個簡單易懂的 README,其中包含現成的最基本的使用示例。 Andrew Healey 寫了一篇關於如何編寫好的 README 的精彩文章,我強烈建議您閱讀。
當您創建了一個很好的 README 來吸引人們前進時,一個文檔網站可能是一個好主意。 在這裡,您可以放置更深入的 API 文檔、典型用例的配方和一個很好的常見問題解答。
有很多很棒的工具可以用來生成文檔網站。 我最喜歡的是來自 Facebook 的docusaurus
(謙虛的吹噓:我們在創建create-react-app
網站時使用了它),但是那裡有幾個不錯的選擇。
我們不會在本文中介紹如何編寫好的文檔。 那裡有幾篇很好的文章——甚至是一個名為“Write the Docs”的社區。 他們寫了一個很好的指南,告訴你如何開始編寫出色的文檔。
概括
通過本系列文章,我們創建了一個相當不錯的驗證庫。 它有一個非常簡單的 API、在您需要時的靈活性、良好的開發人員體驗以及許多非常酷的特性。
我們已經逐步了解了我們是如何實現的,我希望您對如何製作自己的庫以及如何製作人們喜歡使用的庫有更深入的了解。
請在評論中讓我知道您的想法,以及是否有某些部分您被卡住或難以理解。 隨著反饋的不斷湧現,我會盡力更新文章。
結束這篇文章——這是最終版本:
- 請參閱 CodeSandbox 演示
謝謝閱讀!