react-dropzone with web worker
Denys
Posted on May 23, 2024
Motivation
Once I had a weird experience with large images and some stuff like images and react-dropzone
. It was too laggy with large ones and it blocked some UI elements.
Prerequisites
- react and stuff chained with it
- react-dropzone
Problematic
I have written an example with the default preset for react-dropzone
and just trying to upload some large img.
First code block (such as default preset):
import { useCallback, useState } from 'react'
import { useDropzone } from 'react-dropzone'
export const Dropzone = () => {
const [state, setState] = useState<string | undefined>(undefined)
const onDrop = useCallback((acceptedFiles: File[]) => {
acceptedFiles.forEach((file) => {
const reader = new FileReader()
reader.onload = () => {
const result = reader.result as any
setState(result)
}
reader.readAsDataURL(file)
})
}, [])
const { getRootProps, getInputProps } = useDropzone({ onDrop })
return (
<div {...getRootProps()}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
{state && <img src={state} style={{ width: 200, height: 200 }} />}
</div>
)
}
We can try to use input while we trying to upload an image and ... it's gone.
So I tried many things and maybe u can provide ur own solutions to comments or will create another article about it, will appreciate it.
Solution
The solution is to use Web Worker
to unload our operation.
The preload by new Image()
does not work.
Web Worker has no Image
at all, though.
worker.js
onmessage = async function (event) {
const response = await fetch(event.data.result)
const blob = await response.blob()
postMessage(URL.createObjectURL(blob))
}
index.tsx
import { useCallback, useEffect, useState } from 'react'
import { useDropzone } from 'react-dropzone'
export const Dropzone = () => {
const [state, setState] = useState<string | undefined>(undefined)
const [worker, setWorker] = useState<Worker | null>(null)
const onDrop = useCallback(
(acceptedFiles: File[]) => {
acceptedFiles.forEach((file) => {
const reader = new FileReader()
reader.onload = () => {
const result = reader.result as string
if (worker) {
worker.postMessage({ result })
}
}
reader.readAsDataURL(file)
})
},
[worker]
)
const { getRootProps, getInputProps } = useDropzone({ onDrop })
useEffect(() => {
const workerInstance = new Worker(new URL('worker.js', import.meta.url))
workerInstance.onmessage = function ({ data }) {
setState(data)
}
setWorker(workerInstance)
return () => workerInstance.terminate()
}, [])
return (
<div {...getRootProps()}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
{state && <img src={state} style={{ width: 200, height: 200 }} />}
</div>
)
}
Posted on May 23, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.