今天的主題很單純,就是單純上傳圖片,不論是只有一個頁面或是多個功能都要用到,製造一個 hook 是很不虧的選擇,因為也是懶得再寫一次 (¬_¬)
首先,要有地方上傳,input 請出場:
<input type="file" id="file-input" accept="image/*" />
type
定義了要上傳圖片,而 accept
只能上傳 image 相關,如果只想限制 jpg,只要修改成 accept=".jpg"
,相關設定可參閱 MDN
畫面整體會是這樣:
<label htmlFor="file-input">
<input
type="file"
id="file-input"
accept="image/*"
/>
<Button as="span">Upload One Image</Button>
</label>
對 input
加上 display: none
& button
display: inline
就可以再見原生醜UI了
function useImage() {
const inputRef = useRef(null)
const [image, setImage] = useState(null)
const handleUpload = (e) => {
const file = e.target.files[0]
const imageURL = URL.createObjectURL(file)
setImage({ name: file.name, url: imageURL })
}
const handleRemove = () => {
URL.revokeObjectURL(image.url)
setImage(null)
if (inputRef.current) {
inputRef.current.value = ""
}
}
return {
image,
handleUpload,
handleRemove,
inputRef,
}
}
handleUpload
監聽 input onchagne
,會接受 event,透過 input 上傳的檔案會出現在 e.target.files
會是一個 FileList Object,可以進一步用array index取值方式取中特定檔案,目前是只能上傳一張圖片,因此 files[0]
即可,而取出來的也會是 File Object,會有檔案名稱、檔案大小等資訊。URL.createObjectURL
,接收File/Blob
,回傳 ojbectURL
,很易懂的 API,且搭配 URL.revokeObjectURL
在移除圖片時也移除相關 ref 。(API)
handleRemove
移除圖片。inputRef
來移除 input.value
,雖然 display:none
後看不到 input
本身,但原本的上傳檔案的檔名與路徑會存在 input.value
,若只是針對上傳後的圖片進行操作,這個動作就不是那麼必要。好,接下來是多張圖片上傳。。
function useImages() {
const inputRef = useRef()
const [images, setImages] = useState(null)
const handleUpload = (e) => {
const images = [...e.target.files].map((file) => {
return {
name: file.name,
url: URL.createObjectURL(file),
}
})
setImages(images)
}
const handleRemove = (itemIndex) => {
setImages((prev) =>
prev.filter((img, index) => {
if (index === itemIndex) {
URL.revokeObjectURL(img.url)
}
return index !== itemIndex
})
)
}
useEffect(() => {
if (images.length === 0) {
if (inputRef.current) {
inputRef.current.value = ""
}
}
}, [images])
return {
images,
handleUpload,
handleRemove,
inputRef,
}
}
基本上與前一個一模一樣,只是單一內容操作全部改成 Array 操作,不同的是:
FileList
是唯讀 (readonly),當刪除一張圖片時,沒辦法同時對 input
的 value
或其產生的 FileList
操作,因此才會用 useEffect,當照片都被清除時,才真正清空 value
;當然,如果只是針對圖片本身操作,value
清空就不是那麼重要,也可以引導使用者重新上傳(選擇圖片),來確保 value 資料正確。可以再進一步傳入檔案大小上的限制 (請參閱 File.size),來抵擋使用者上傳太大的照片。
我家的餅餅好可愛 (‘∀’●)♡