😊
Formik,Yup,Apolloで非同期バリーデーション作った
はじめに
この記事は mediba Advent Calendar 2021 19日目の記事です
株式会社medibaでフロントエンジニアをしているyangです。
弊社サービスのCMS開発でBEによるファイルのバリーデーションする必要がありました。
関連する一部のスタックは:
- Formik
- Yup
- Apollo
やりたいこと:
- ファイルのアップロード = バリーデーションAPIを叩く
- 同じファイル2回アップしてもバリデーションを行う(中身の変更とか)
- 新たにアップロードされてない限りに、前のバリーデーション結果が出力されている
ファイルアップロードする際の処理
ファイルのアップロード = バリーデーションAPIを叩く ✅
FormikのFieldをカスタマイズして、ファイルアップロードされた際の制御を入れます。
const handleFileChange = React.useCallback(
async (event: any) => {
if (event.target.files && event.target.files[0]) {
const file = event.target.files[0];
setLoading(true);
await client.query({
query: validationDocument,
variables: {
fileToBeCheck: file,
},
fetchPolicy: 'network-only',
});
setLoading(false);
form.setFieldValue(fileInput, file);
form.setFieldTouched(fileInput, true); //Fieldが触れる前にバリーデーション発火しない
}
},[]);
ここではfetchPolicyをnetwork-onlyに設定し、
単純にAPIを叩いて、リスポンスをキャッシュに保存しています。
ファイル名に限らず、同じファイル2回アップしてもバリデーションが行う✅
inputを騙すため、下記のonClickイベントを作ります。
const onClickTrick = React.useCallback(
(event: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
event.currentTarget.value = '';
},
[]
);
忘れずに、inputに組み付け
<input ...name={fileInput} onClick={onClickTrickInput} onChange={handleFileChange}/>
バリーデーション関数を作る
新たにアップロードされてない限りに、前のバリーデーション結果が出力されている✅
中身は
バリーデーション結果をreadQueryでキャッシュから読み込みだけになります
const customizedValidator = (file: File, context: TestContext) => {
const validationResult = client.readQuery({
query: validationDocument,
variables: {
fileToBeCheck: file,
},
});
if (validationResult) {
const errorMessage = validationResult?.error_message;
if (validationResult?.success) return true;
if (Array.isArray(errorMessage) && errorMessage.length > 0) {
return context.createError({
message: errorMessage,
});
}
}
return false; // apollo clientでエラーハンドリングしてるので、ここでfalseだけ返す
};
yupで加工
Yup.object({
fileInput: yup.mixed().test(
(file, context) =>
file ? customizedValidator(file,context) : true
),
})
最後に
今回はかなり見たことないやり方でやってしまいましたw、
他のやり方もたくさんあると信じづづ、
ベストプラクティスではなくても、クリエティブ的でありたい
Discussion