⚛️
Reactの関数コンポーネントとFormikを使用して画像をブラウザにアップロードする
概要
関数コンポーネントと Formik を使用して画像をブラウザにアップロードする方法についてまとめます。
ソースコードは以下
codesandbox は以下
実装
全体像
実装の全体は以下のようになります(./src/App.tsx
)。
3つのコンポーネント(Thumb
、ImageUploader
、App
)から構成されています。
順を追って説明していきます。
import { FC, useState, useEffect } from "react";
import { Formik } from "formik";
import * as yup from "yup";
type ThumbProps = {
file: File | null;
};
const Thumb: FC<ThumbProps> = ({ file }) => {
const [loading, setLoading] = useState<Boolean>(true);
const [thumb, setThumb] = useState<string>();
useEffect(() => {
const reader = new FileReader();
if (file) {
reader.readAsDataURL(file);
reader.onload = () => {
setThumb(reader.result as string);
};
setLoading(false);
}
}, [file]);
if (!file) {
return null;
}
if (loading) {
return <p>Loading....</p>;
}
return (
<img
src={thumb}
alt={file.name}
className="img-thumbnail mt-2"
height={200}
width={200}
/>
);
};
const ImageUploader: FC = () => {
return (
<Formik
initialValues={{ file: null }}
onSubmit={(values: any) => {
alert(
JSON.stringify({
fileName: values.file.name,
type: values.file.type,
size: `${values.file.size} bytes`,
})
);
}}
validationSchema={yup.object().shape({
file: yup.mixed().required(),
})}
>
{({ handleSubmit, setFieldValue, values }) => {
return (
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="file">File upload</label>
<input
type="file"
id="file"
name="file"
onChange={(event) => {
setFieldValue(
"file",
event.currentTarget.files !== null
? event.currentTarget.files[0]
: null
);
}}
className="form-control"
/>
<Thumb file={values.file} />
</div>
<button type="submit" className="btn btn-primary">
submit
</button>
</form>
);
}}
</Formik>
);
};
const App: FC = () => {
return (
<div className="container">
<ImageUploader />
</div>
);
};
export default App;
コンポーネント
Thumb コンポーネント
Thumb
コンポーネントは、<input type="file">
から受け取った画像をサムネイルとして表示するコンポーネントです。
useEffect
で file の変換を検知したときに、ファイルを読み込みsetThumb
でサムネイルを設定します。
const Thumb: FC<ThumbProps> = ({ file }) => {
// useStateで画像の読み込みとファイルの状態を設定
const [loading, setLoading] = useState<Boolean>(true);
const [thumb, setThumb] = useState<string>();
// ファイルに変化があったときに、状態を更新する
useEffect(() => {
const reader = new FileReader();
if (file) {
reader.readAsDataURL(file);
reader.onload = () => {
setThumb(reader.result as string);
};
setLoading(false);
}
}, [file]);
// ファイルがなければ空を返す
if (!file) {
return <></>;
}
// ローディング中は、以下のコンポーネントを返す
if (loading) {
return <p>Loading....</p>;
}
// ファイルがある + ローディング完了 のとき<img>を返す
return (
<img
src={thumb}
alt={file.name}
className="img-thumbnail mt-2"
height={200}
width={200}
/>
);
};
ImageUploader コンポーネント
ImageUploader
コンポーネントは、Fomik
を使って画像アップロードする form のコンポーネントです。
Thumb
コンポーネントの引数にvalues.file
で画像を渡しています。
onSubmit
で画像の情報について表示するようにしています。この箇所を変更することで S3 などに画像を送信できます。
const ImageUploader: FC = () => {
return (
<Formik
initialValues={{ file: null }}
onSubmit={(values: any) => {
alert(
JSON.stringify({
fileName: values.file.name,
type: values.file.type,
size: `${values.file.size} bytes`,
})
);
}}
validationSchema={yup.object().shape({
file: yup.mixed().required(),
})}
>
{({ setFieldValue, values }) => {
return (
<Form>
<div className="form-group">
<label htmlFor="file">File upload</label>
<input
type="file"
id="file"
name="file"
onChange={(event) => {
setFieldValue(
"file",
event.currentTarget.files !== null
? event.currentTarget.files[0]
: null
);
}}
className="form-control"
/>
<Thumb file={values.file} />
</div>
<button type="submit" className="btn btn-primary">
submit
</button>
</Form>
);
}}
</Formik>
);
};
App コンポーネント
App
コンポーネントです。ImageUploader
を表示するだけです。
const App: FC = () => {
return (
<div className="container">
<ImageUploader />
</div>
);
};
動作確認
環境
- node
- v14.15.4
- yarn, ts-node, @types/node グローバルインストール済み
下準備
git clone https://github.com/Msksgm/react-image-upload-example.git
cd react-image-upload-example
yarn
実行
yarn start
画面確認
「ファイルを選択」ボタンを押下して、画像を選ぶと以下のように表示されます。
参考
Discussion