Twitter みたいに画像を投稿できるフォームでは input[type="file"]
を使いますがデフォルトでは選択した画像のファイル名が表示されるだけなので、何の画像を選択したかプレビューができたほうが便利です。
割とフォーム作成の際に必要になるので思いつきで React のコンポーネント化してみましたのメモ。
JavaScript FileAPI で input[type="file"]
で選択されている画像のプレビューを表示する方法
HTML
<input type="file" id="inputFile">
const inputfile = document.getElementById('inputFile') const image = document.createElement('img') inputfile.addEventListener("change", function(evt) { if (!evt.target.files || !evt.target.files[0]) { image.remove(); return; } const file = evt.target.files[0]; const reader = new FileReader(); // reader.readAsDataURL でファイルの読み込みが完了したら <img> タグにして出力する reader.onload = () => { image.src = reader.result; inputfile.insertAdjacentElement('afterend', image); } reader.readAsDataURL(file); }, false);
サンプル
See the Pen File Preview by KIKIKI (@kikiki_kiki) on CodePen.
👇 React Component 化する
File API を使ってプレビュー画像を表示する React Component
// ImagePreview.tsx import { FC, useEffect, useState } from 'react'; type ImagePreviewProps = { file: File | null }; export const ImagePreview: FC<ImagePreviewProps> = ({ file, ...props }) => { const [url, setUrl] = useState<string>(''); const isLoading = file && !url; useEffect(() => { if (!file) { return; } let reader: FileReader | null = new FileReader(); reader.onloadend = () => { const res = reader!.result; if (res && typeof res === 'string') { setUrl(res); } }; reader.readAsDataURL(file); return () => { reader = null; }; }, [file]); return file ? ( isLoading ? ( <Skelton /> {/* Loader */} ) : ( <img src={url} alt={file.name} {...props} /> ) ) : null; };
App.tsx
import { FC, useCallback, useState } from 'react'; import { ImagePreview } from './ImagePreview'; const App: FC = () => { const [file, setFile] = useState<File | null>(null) const changeFileHandler = useCallback((evt: React.ChangeEvent<HTMLInputElement>) => { if (evt.currentTarget?.files && evt.currentTarget.files[0]) { setFile(evt.currentTarget.files[0]); } }, []); const resetHandler = useCallback(() => { setFile(null); }, []) return ( <div> <input type="file" onChange={changeFileHandler} /> {file && ( <div> <button type="button" aria-label="Close" onClick={resetHandler} /> <ImagePreview file={file} /> </div> )} </div> ); };
サンプル
See the Pen File Preview with React by KIKIKI (@kikiki_kiki) on CodePen.
所感
FileAPI を使ってプレビュー表示機能はなんか毎回都度作ってて、その度にどうやるんだっけ?って忘れる…
React Component はぱっと思いつきで作ったものなので、もっといい方法がありそうな気がする。Component を返す Hooks にして isLoading とかを別に取れるようにしたほうが便利かな〜
まぁ動いてるから良し ₍ ᐢ. ̫ .ᐢ ₎
[参考]
- FileReader - Web APIs | MDN
- JavaScript FileAPIについて学ぶ - Qiita
- New React Hooks Pattern? Return a Component - DEV Community
- Useful Patterns by Use Case | React TypeScript Cheatsheets
- Uploading Files with React.js | Pluralsight