React+Next.js+TypeScript+firebaseで画像アップローダを作った
前回、React+Next.js+firebaseで画像アップローダを作りました。
React+Next.js+firebaseで画像アップローダを作った - あおいろメモ
今回はこれをTypeScriptで書いてみます。
こんな超シンプルなアップローダができます
前回同様、firebaseの認証等は、「react.js&next.js超入門 第2版」を参考に、 components/fire.jsに保存しました。
index.tsx
// このサイト参考に画像アップロードサイト作成 // How to do image upload with firebase in react. // https://dev.to/itnext/how-to-do-image-upload-with-firebase-in-react-cpj // これはJSで係れているのでTSに直した // 他参考書籍・サイト // 『firebase公式ドキュメント』 // https://firebase.google.com/docs/storage/web/start?hl=ja //『react.js&next.js超入門 第2版』 //https://www.shuwasystem.co.jp/book/9784798063980.html // 『はじめてつくるReactアプリ with TypeScript』 // https://www.amazon.co.jp/dp/B094Z1R281 import {useState} from 'react'; import firebase from "firebase" import Head from 'next/head' import "../../components/fire" const storage= firebase.storage() const Index=()=> { return ( <div> <Head> <title>TS Test</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" crossOrigin="anonymous"></link> </Head> <Upload /> </div> ) } // 型定義 type AllInputsType = { imgUrl: string } const Upload = ()=>{ const allInputs = {imgUrl: ''} const [imageAsFile, setImageAsFile] = useState<any>('') const [imageAsUrl, setImageAsUrl] = useState<AllInputsType>(allInputs) // Eventの型は一回間違えてからエラーが出たところに // VSCode上でオーバーレイしてヒントを出すとわかる。 // ただanyでいいかも。 const handleImageAsFile=(e:React.ChangeEvent<HTMLInputElement>)=>{ console.log(e) const image = e.target.files[0] setImageAsFile(imageFile => (image)) } const handleFireBaseUpload=(e:React.FormEvent<HTMLFormElement>)=>{ e.preventDefault() console.log('start of upload') // async magic goes here... if(imageAsFile === '' || imageAsFile === undefined) { console.error(`not an image, the image file is a ${typeof(imageAsFile)}`) } else { const uploadTask = storage.ref(`/images/${imageAsFile.name}`).put(imageAsFile) //initiates the firebase side uploading //https://firebase.google.com/docs/storage/web/upload-files?hl=ja#monitor_upload_progress //このドキュメントだけ呼んだだけだと分かりにくいが、 //uploadTask.onの第1引数に'state_changed'を指定すると //第2、第3、第4引数に渡した関数で // アップロード状況を管理することができる。 uploadTask.on('state_changed', (snapShot) => { //takes a snap shot of the process as it is happening console.log(snapShot) }, (err) => { //catches the errors console.log(err) }, () => { // gets the functions from storage refences the image storage in firebase by the children // gets the download url then sets the image from firebase as the value for the imgUrl key: storage.ref('images').child(imageAsFile.name).getDownloadURL() .then(fireBaseUrl => { //オブジェクトのスプレッド構文による展開と、値の更新 //{imgUrl: 古いURL}を{imgUrl: 新しいURL}に更新している。 //https://qiita.com/FumioNonaka/items/58358a29850afd7a0f37 setImageAsUrl(prevObject => ({...prevObject, imgUrl: fireBaseUrl})) }) }) } } return <div> <form className="form-group" onSubmit={handleFireBaseUpload}> <input className="form-control-file mb-1" // allows you to reach into your file directory and upload image to the browser type="file" onChange={handleImageAsFile} /> <button className="btn btn-primary">アップロード</button> </form> <img src={imageAsUrl.imgUrl} alt="image tag" /> </div> }; export default Index
Eventの型について
onChange
やonSubmit
に関数を指定しますが、const 関数=(e:型)=>{}
の形で、この関数に渡されるe
(Event)の型を定義しなければなりません。これについては
このようにわざとstring
のような間違えた型をつけます。そうすると
JSX内のonChange
に赤波が引かれ、そのエラーにマウスをオーバーレイすると、型ヒントが出てきます。今回の場合Eventの正しい型はReact.ChangeEvent<HTMLInputElement>
でした。(Next.jsの場合暗黙にReact
がimport
されているので、名前空間としてReact.
を前につけないといけないと思われる。)
ただ、
TypeScript の型定義に凝りすぎじゃね? - Neo's World
に述べられているようにあまり型にこだわりすぎず、any
に逃げた方がいいと思われます。
『はじめてつくるReactアプリ with TypeScript』でよくあった、自分で定義したオブジェクトの型を明示的に示すくらいがちょうどいいのかもしれないです。(勉強不足)