React Hook Formで画像アップロード機能を作るとエラーで怒られたので忘備録
この記事について
React Hook Form で画像アップロード機能を実装しました。
実装している際に、エラーが出てその解決に時間を溶かしてしまったのでその過ちを繰り返さないために残しておきます。
InvalidStateError: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
環境
- フレームワーク: Next.js 11.0.1
- CSS in JS: ChakraUI 1.6.5
- フォーム周り: react-hook-form 7.10.1
やりたいこと
<input type="file />
を使用し、画像を選択するとプレビュー欄に表示される。
NGコード
react-hook-formのref
などをregister()
で渡していて、画像を選択したらsetValue()
で選択した画像パスを格納し、それをプレビューとして表示させるイメージです。
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { files } = event.target;
setValue('preview_url', URL.createObjectURL(files[0])); // ここでエラーが出た
};
const fileUploadExample = () => {
return (
<>
<Avatar
size="m"
src={watch('preview_url')}
/>
<Input
{...ragister('preview_url')}
type="file"
accept="image/*"
onChange={onFileInputChange}
/>
</>
);
};
動いたコード
react-hook-formのref
は渡さずに、useRef()
を渡すと動きました。
const inputRef = useRef<HTMLInputElement>(null);
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { files } = event.target;
setValue('preview_url', URL.createObjectURL(files[0])); // 動いた!!
};
const { name: previewUrl } = register('preview_url');
const fileUploadExample = () => {
return(
<>
<Avatar
size="m"
src={watch('preview_url')}
/>
<Input
id={previewUrl}
ref={inputRef}
name={previewUrl}
type="file"
accept="image/*"
onChange={onFileInputChange}
/>
</>
);
};
原因
<input type="file" />
にはvalueが設定できないにもかかわらず、valueをセットしようとしていることが原因でした。
Reactドキュメントにもちゃんと書かれていました。
Reactでは、値はユーザーのみが設定でき、プログラムでは設定できないため、常に制御されていないコンポーネントとなります。
https://reactjs.org/docs/uncontrolled-components.html#the-file-input-tag
react-hook-formのref
をregister()
を使ってDOMにセットし、画像選択時にsetValue()
を行うと、<input type="file">
のDOMにvalueがセットされるためエラーが出ていたようです。
useRef()
を使用した場合だと、setValue()
時にそのDOMにvalueをセットしない為うまくいっているようでした。
まとめ
分かってしまえばそりゃそうなるよなという感じですが、ふんわりとした認識でsetValue()
やref
を使っていたためエラー解決に時間を溶かしてしまい辛い気持ちになりました...。改めてドキュメントを読むことの大切さを実感。
ref周りは難しいので(特にforwardRefとか)もう少し時間をかけて学んでいきたいところ。
基礎は大事!!!!
おまけ
setValue()
の実装コードは以下のようになっていました。
難しくてよくわかりませんでしたが、きっとこの中でref.current.value = value
をしているんだと思う。
const setValue = (name, value, options = {}) => {
const field = get(fieldsRef.current, name);
const isFieldArray = namesRef.current.array.has(name);
if (isFieldArray) {
subjectsRef.current.array.next({
values: value,
name,
isReset: true,
});
if ((readFormStateRef.current.isDirty ||
readFormStateRef.current.dirtyFields) &&
options.shouldDirty) {
set(formStateRef.current.dirtyFields, name, setFieldArrayDirtyFields(value, get(defaultValuesRef.current, name, []), get(formStateRef.current.dirtyFields, name, [])));
subjectsRef.current.state.next({
name,
dirtyFields: formStateRef.current.dirtyFields,
isDirty: getIsDirty(name, value),
});
}
!value.length &&
set(fieldsRef.current, name, []) &&
setValue(fieldArrayDefaultValuesRef.current, name, []);
}
((field && !field._f) || isFieldArray) && !isNullOrUndefined(value)
? setInternalValues(name, value, isFieldArray ? {} : options)
: setFieldValue(name, value, options, true, !field);
isFieldWatched(name) && subjectsRef.current.state.next({});
subjectsRef.current.watch.next({ name, values: getValues() });
};
参考
https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/file
https://github.com/react-hook-form/react-hook-form/issues/4765
https://stackoverflow.com/questions/1696877/how-to-set-a-value-to-a-file-input-in-html/1696884#1696884
https://reactjs.org/docs/uncontrolled-components.html#the-file-input-tag
Author And Source
この問題について(React Hook Formで画像アップロード機能を作るとエラーで怒られたので忘備録), 我々は、より多くの情報をここで見つけました https://qiita.com/ke_na/items/acd0640e1fb1f5c2d69d著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .