Recently, I was working on a project and I had to make multiple file inputs. The thing is every input should take only one file with certain type which in my case was pdf , jpeg only. After uploading the file, user should submit all the uploaded files together using only one submit button. In my case, user doesn't have to upload all the files at once.
The first thing came to my mind is to use FormData
; however, I had to submit an Array of objects each object should have file_Id and the file itself and let's call it uploaded_file in our example. With FormData
I couldn't do that, so I had make it in my own way.
I assume in this example that you are familiar with Reactjs and hooks.
Here are the steps I followed to achieve my goal:
1. Create React component with 3 input files, each input file accepts only pdf , jpeg with unique Id. Also, we want 1 submit button.
import React from 'react';
const MultipleFileInput = () => {
return (
<form className="upload--container">
<div className="upload--button">
<input id={1} accept=".jpeg, .pdf" type="file" />
</div>
<div className="upload--button">
<input id={2} accept=".jpeg, .pdf" type="file" />
</div>
<div className="upload--button">
<input id={3} accept=".jpeg, .pdf" type="file" />
</div>
<button type="submit">Submit</button>
</form>
);
};
export default MultipleFileInput;
2. create state that will hold the Array of objects.
// state that will hold the Array of objects
// initialized with empty array
const [files, setFiles] = useState([]);
3. Add onChageHandler for each input file. To read these files I have used FileReader
Read More About FileReader Web API
// onChange function that reads files on uploading them
// files read are encoded as Base64
function onFileUpload(event) {
event.preventDefault();
// Get the file Id
let id = event.target.id;
// Create an instance of FileReader API
let file_reader = new FileReader();
// Get the actual file itself
let file = event.target.files[0];
file_reader.onload = () => {
// After uploading the file
// appending the file to our state array
// set the object keys and values accordingly
setFiles([...files, { file_id: id, uploaded_file: file_reader.result }]);
};
// reading the actual uploaded file
file_reader.readAsDataURL(file);
}
4. now let's implement our submit button, for this example we will just console log the results; however, I had to send these files to the server.
// handle submit button for form
function handleSubmit(e) {
e.preventDefault();
console.log(files)
}
5. Finally, Let's add some restrictions to our logic. For example, disable submit button if no file was uploaded.
// button state whether it's disabled or enabled
const [enabled, setEnabled] = useState(false);
// using useEffect we can detect if user uploaded any file,
// so enable submit button
useEffect(() => {
if (files.length === 0) {
setEnabled(false);
} else {
setEnabled(true);
}
}, [files]);
// render submit button based on its state.
{enabled ? (
<button type="submit">Submit</button>
) : (
<button disabled type="submit">
Submit
</button>
)}
This will be the whole code after all.
codesandox Link
import React, { useState, useEffect } from 'react';
const MultipleFileInput = () => {
// state that will hold the Array of objects
// initialized with empty array
const [files, setFiles] = useState([]);
// onChange function that reads files on uploading them
// files read are encoded as Base64
function onFileUpload(event) {
event.preventDefault();
// Get the file Id
let id = event.target.id;
// Create an instance of FileReader API
let file_reader = new FileReader();
// Get the actual file itself
let file = event.target.files[0];
file_reader.onload = () => {
// After uploading the file
// appending the file to our state array
// set the object keys and values accordingly
setFiles([...files, { file_id: id, uploaded_file: file_reader.result }]);
};
// reading the actual uploaded file
file_reader.readAsDataURL(file);
}
// handle submit button for form
function handleSubmit(e) {
e.preventDefault();
console.log(files);
}
// button state whether it's disabled or enabled
const [enabled, setEnabled] = useState(false);
// using useEffect we can detect if user uploaded any file,
// so enable submit button
useEffect(() => {
if (files.length === 0) {
setEnabled(false);
} else {
setEnabled(true);
}
}, [files]);
return (
<form onSubmit={handleSubmit} className="upload--container">
<h1> Multiple File Inputs with Signle Submit Button </h1>
<div className="upload--button">
<input
onChange={onFileUpload}
id={1}
accept=".jpeg, .pdf"
type="file"
/>
</div>
<div className="upload--button">
<input
onChange={onFileUpload}
id={2}
accept=".jpeg, .pdf"
type="file"
/>
</div>
<div className="upload--button">
<input
onChange={onFileUpload}
id={3}
accept=".jpeg, .pdf"
type="file"
/>
</div>
{enabled ? (
<button type="submit">Submit</button>
) : (
<button disabled type="submit">
Submit
</button>
)}
</form>
);
};
export default MultipleFileInput;
Final Words,
I will be glad if someone shared different approach, or any modifications to my current implementation. So, please don't hesitate to share your thoughts.
Top comments (11)
Great! I will give it a try in my next project.
Cool!
Let me know if you needed any help my friend.
Thank you so much!!! I couldn't find a solution anywhere and I was about to give up. Much appreciated.
Hey dude, thanks for that. I started learn react this month and I understand you code and your article. Great!
Heyy buddy, I'm glad that liked my article.
Let me know if you needed any help ! :)
Happy Coding !
That's great!!
That must have taken you a lot of search
Yeah it did, but it was worth it.
Thank you <3
Wow, this is great. This really helps, PLS can you adjust this code for just one input type file instead of multiple
Thank Fadi, great tutorial
How do i add a button to add more files after uploading one file?
Thank you so much. You made my day.