React Integration
Example code showing how to integrate MultipleUpload with a React component using useEffect and useRef.
// React Component: FileUploader.jsx
import React, { useEffect, useRef } from 'react';
import MultipleUpload from 'multipleupload';
function FileUploader({ uploadUrl, onComplete }) {
const containerRef = useRef(null);
const uploaderRef = useRef(null);
useEffect(() => {
uploaderRef.current = new MultipleUpload(containerRef.current, {
uploadUrl: uploadUrl,
onTaskComplete: (task) => {
if (onComplete) onComplete(task);
}
});
return () => {
// Cleanup on unmount
if (uploaderRef.current && uploaderRef.current.destroy) {
uploaderRef.current.destroy();
}
};
}, [uploadUrl]);
return (
<div ref={containerRef} className="mu-uploader">
<button type="button" className="mu-select-btn">Select Files</button>
<input type="file" className="mu-file-input" multiple style={{ display: 'none' }} />
<div className="mu-dropzone">
<div className="mu-dropzone-text">Drag & drop files here</div>
</div>
<div className="mu-queue"></div>
</div>
);
}
export default FileUploader; // Usage in your App
import FileUploader from './FileUploader';
function App() {
return (
<FileUploader
uploadUrl="/api/upload"
onComplete={(task) => console.log('Uploaded:', task.name)}
/>
);
}
Why a ref and not state
The uploader owns a piece of DOM and manages it imperatively, which is the one thing React does not want to re-render underneath. Holding the container in a ref and the instance in another keeps it outside the render cycle entirely, so React never replaces nodes the component is still using.
The cleanup that matters
Returning destroy() from useEffect is not optional. Without it, navigating away mid-upload leaves listeners bound to detached nodes and a transfer running with nowhere to report to. In development, React 18's StrictMode mounts effects twice on purpose — so a missing cleanup shows up immediately as two uploaders in one container, which is a useful early warning rather than a nuisance.
useCallback or keep it in a ref.