独自のReact検証ライブラリの作成:機能(パート2)

公開: 2022-03-10
簡単な要約↬Kristoferの前回の記事で、検証ライブラリの基本的な部分を実装する方法について説明しました。 次のパートでは開発者エクスペリエンスの向上に焦点を当てますが、今日の記事では、パート1で作成されたものに機能を追加することに焦点を当てます。

検証ライブラリの実装はそれほど難しくありません。 また検証ライブラリを他のライブラリよりもはるかに優れたものにするこれらの追加機能をすべて追加しているわけでもありません。

この記事では、この一連の記事の前の部分で実装を開始した検証ライブラリを引き続き実装します。 これらは、単純な概念実証から実際に使用可能なライブラリに私たちを連れて行く機能です!

  • パート1:基本
  • パート2:機能
  • パート3:経験

送信時にのみ検証を表示する

すべての変更イベントを検証しているため、ユーザーエクスペリエンスを向上させるには、ユーザーエラーメッセージの表示が早すぎます。 これを軽減する方法はいくつかあります。

最初の解決策は、 submittedたフラグをuseValidationフックの戻りプロパティとして提供することです。 このようにして、エラーメッセージを表示する前に、フォームが送信されたかどうかを確認できます。 ここでの欠点は、「エラーコードの表示」が少し長くなることです。

 <label> Username <br /> <input {...getFieldProps('username')} /> {submitted && errors.username && ( <div className="error">{errors.username}</div> )} </label>

もう1つのアプローチは、2番目のエラーセット( submittedErrorsと呼びましょう)を提供することです。これは、 submittedたものがfalseの場合は空のオブジェクトであり、trueの場合はerrorsオブジェクトです。 次のように実装できます。

 const useValidation = config => { // as before return { errors: state.errors, submittedErrors: state.submitted ? state.errors : {}, }; }

このようにして、表示したいタイプのエラーを簡単に分解できます。 もちろん、これはコールサイトでも実行できますが、ここで提供することで、すべてのコンシューマー内ではなく、一度だけ実装します。

  • submittedErrorsの使用方法を示すCodeSandboxデモを参照してください。
ジャンプした後もっと! 以下を読み続けてください↓

エラーメッセージをオンに表示-ぼかし

多くの人は、特定のフィールドを離れるとエラーが表示されることを望んでいます。 上記のsubmittedErrorsと同様に、どのフィールドが「ぼやけている」(離れてナビゲートされている)かを追跡し、オブジェクトblurredErrorsを返すことで、これに対するサポートを追加できます。

実装では、新しいアクションタイプ( blur )を処理する必要があります。これにより、 blurred :と呼ばれる新しい状態オブジェクトが更新されます。

 const initialState = { values: {}, errors: {}, blurred: {}, submitted: false, }; function validationReducer(state, action) { switch (action.type) { // as before case 'blur': const blurred = { ...state.blurred, [action.payload]: true }; return { ...state, blurred }; default: throw new Error('Unknown action type'); } }

blurアクションをディスパッチするとき、フィールド名をキーとしてblurredた状態のオブジェクトに新しいプロパティを作成し、そのフィールドがぼかしていることを示します。

次のステップは、 getFieldProps関数にonBlurプロップを追加することです。これにより、該当する場合にこのアクションがディスパッチされます。

 getFieldProps: fieldName => ({ // as before onBlur: () => { dispatch({ type: 'blur', payload: fieldName }); }, }),

最後に、 useValidationフックからblurredErrorsを提供して、必要な場合にのみエラーを表示できるようにする必要があります。

 const blurredErrors = useMemo(() => { const returnValue = {}; for (let fieldName in state.errors) { returnValue[fieldName] = state.blurred[fieldName] ? state.errors[fieldName] : null; } return returnValue; }, [state.errors, state.blurred]); return { // as before blurredErrors, };

ここでは、フィールドがぼやけているかどうかに基づいて、どのエラーを表示するかを判断するメモ化関数を作成します。 エラーまたはぼやけたオブジェクトが変更されるたびに、この一連のエラーを再計算します。 useMemoフックの詳細については、ドキュメントをご覧ください。

  • CodeSandboxデモを見る

小さなリファクタリングの時間

useValidationコンポーネントは、3セットのエラーを返しています。そのほとんどは、ある時点で同じように見えます。 このルートをたどる代わりに、ユーザーがフォームのエラーを表示するタイミングを構成で指定できるようにします。

新しいオプションshowErrorsは、「submit」(デフォルト)、「always」、または「blur」のいずれかを受け入れます。 必要に応じて、後でオプションを追加できます。

 function getErrors(state, config) { if (config.showErrors === 'always') { return state.errors; } if (config.showErrors === 'blur') { return Object.entries(state.blurred) .filter(([, blurred]) => blurred) .reduce((acc, [name]) => ({ ...acc, [name]: state.errors[name] }), {}); } return state.submitted ? state.errors : {}; } const useValidation = config => { // as before const errors = useMemo( () => getErrors(state, config), [state, config] ); return { errors, // as before }; };

エラー処理コードがスペースの大部分を占め始めたので、それを独自の関数にリファクタリングしています。 Object.entries.reduceのものに従わない場合、それは問題ありません。これは、前のセクションのfor...inコードを書き直したものです。

onBlurまたはインスタント検証が必要な場合は、 useValidation構成オブジェクトでshowErrorプロップを指定できます。

 const config = { // as before showErrors: 'blur', }; const { getFormProps, getFieldProps, errors } = useValidation(config); // errors would now only include the ones that have been blurred
  • CodeSandboxデモを見る

仮定に関する注記

「現在、各フォームが同じ方法でエラーを表示することを想定していることに注意してください(常に送信時、常にぼかし時など)。 これはほとんどのアプリケーションに当てはまるかもしれませんが、おそらくすべてではありません。 前提条件を認識することは、APIを作成する上で非常に重要です。」

相互検証を許可する

検証ライブラリの非常に強力な機能は、相互検証を可能にすることです。つまり、あるフィールドの検証を別のフィールドの値に基づいて行うことができます。

これを可能にするには、カスタムフックがオブジェクトではなく関数を受け入れるようにする必要があります。 この関数は、現在のフィールド値で呼び出されます。 それを実装するのは実際にはたった3行のコードです!

 function useValidation(config) { const [state, dispatch] = useReducer(...); if (typeof config === 'function') { config = config(state.values); } }

この機能を使用するには、構成オブジェクトをuseValidationに返す関数を渡すだけです。

 const { getFieldProps } = useValidation(fields => ({ password: { isRequired: { message: 'Please fill out the password' }, }, repeatPassword: { isRequired: { message: 'Please fill out the password one more time' }, isEqual: { value: fields.password, message: 'Your passwords don\'t match' } } }));

ここでは、 fields.passwordの値を使用して、2つのパスワードフィールドに同じ入力が含まれていることを確認します(これはひどいユーザーエクスペリエンスですが、別のブログ投稿用です)。

  • ユーザー名とパスワードを同じ値にしないCodeSandboxデモを参照してください。

アクセシビリティの勝利を追加する

フィールドの小道具を担当しているときに行うべき良いことは、デフォルトで正しいaria-tagsを追加することです。 これは、スクリーンリーダーがフォームを説明するのに役立ちます。

非常に簡単な改善は、フィールドにエラーがある場合にaria-invalid="true"を追加することです。 それを実装しましょう:

 const useValidation = config => { // as before return { // as before getFieldProps: fieldName => ({ // as before 'aria-invalid': String(!!errors[fieldName]), }), } };

これは追加された1行のコードであり、スクリーンリーダーユーザーにとってはるかに優れたユーザーエクスペリエンスです。

なぜString(!!state.errors[fieldName])と書くのか不思議に思うかもしれません。 state.errors[fieldName]は文字列であり、二重否定演算子はブール値を提供します(真偽の値だけではありません)。 ただし、 aria-invalidプロパティは文字列である必要があるため(「true」または「false」に加えて「grammar」または「spelling」も読み取ることができます)、そのブール値を同等の文字列に強制変換する必要があります。

アクセシビリティを改善するためにできる調整はまだいくつかありますが、これは公正なスタートのようです。

簡略検証メッセージの構文

calidatorsパッケージのほとんどのバリデーター(および他のほとんどのバリデーター)は、エラーメッセージのみを必要とします。 その文字列を含むmessageプロパティを持つオブジェクトの代わりに、その文字列を渡すことができたら素晴らしいと思いませんか?

これをvalidateField関数に実装しましょう。

 function validateField(fieldValue = '', fieldConfig, allFieldValues) { for (let validatorName in fieldConfig) { let validatorConfig = fieldConfig[validatorName]; if (typeof validatorConfig === 'string') { validatorConfig = { message: validatorConfig }; } const configuredValidator = validators[validatorName](validatorConfig); const errorMessage = configuredValidator(fieldValue); if (errorMessage) { return errorMessage; } } return null; }

このようにして、検証構成を次のように書き直すことができます。

 const config = { username: { isRequired: 'The username is required', isEmail: 'The username should be a valid email address', }, };

ずっときれい!

初期フィールド値

場合によっては、すでに入力されているフォームを検証する必要があります。 私たちのカスタムフックはまだそれをサポートしていません—それではそれを手に入れましょう!

初期フィールド値は、各フィールドの構成のプロパティinitialValueで指定されます。 指定しない場合、デフォルトで空の文字列になります。

関数getInitialStateを作成します。これにより、レデューサーの初期状態が作成されます。

 function getInitialState(config) { if (typeof config === 'function') { config = config({}); } const initialValues = {}; const initialBlurred = {}; for (let fieldName in config.fields) { initialValues[fieldName] = config.fields[fieldName].initialValue || ''; initialBlurred[fieldName] = false; } const initialErrors = validateFields(initialValues, config.fields); return { values: initialValues, errors: initialErrors, blurred: initialBlurred, submitted: false, }; }

すべてのフィールドを調べ、それらにinitialValueプロパティがあるかどうかを確認し、それに応じて初期値を設定します。 次に、これらの初期値をバリデーターで実行し、初期エラーも計算します。 初期状態オブジェクトを返します。これは、 useReducerフックに渡すことができます。

フィールド設定に非バリデーター小道具を導入しているので、フィールドを検証するときにそれをスキップする必要があります。 そのために、 validateField関数を変更します。

 function validateField(fieldValue = '', fieldConfig) { const specialProps = ['initialValue']; for (let validatorName in fieldConfig) { if (specialProps.includes(validatorName)) { continue; } // as before } }

このような機能を追加し続けると、それらをspecialProps配列に追加できます。

  • CodeSandboxデモを見る

まとめ

すばらしい検証ライブラリを作成する準備が整っています。 私たちはたくさんの機能を追加しました、そして私たちは今ではかなり考え抜かれたリーダーです。

このシリーズの次のパートでは、検証ライブラリをLinkedInでさらにトレンドにするこれらの追加機能をすべて追加します。