Let's Create a Drag & Drop Image Uploader on React 02

List Of Content

ADS Area (CARBON)

ADS Area (CARBON)

 

 

Overview

So after creating the project boilerplate and set up halfway through the draggable image uploader in the previous tutorial, now we need to add the features to the uploading container and add support for multiple files to get uploaded to the server End-Point.

If you haven't watched the last tutorial you can follow it from Here.

Also, you can get the Full Source code on Github.

We two modules for this tutorial.

Lodash functions utility:

npm install lodash --save

And React Icons Kit (large set of icons usable on React)

npm install react-icons-kit --save 

 

Load Image Files

For loading image files from the user machine to the actual webpage, we have to keep it in an array on the state so we could manage it very well.

So, on the state add an empty array just for initialization.

...
this.state = {
  loadedFiles: []
};
...

We need control methods to allow us to manipulate the loaded files state (Add, remove, update...) which is very important when it comes to loading new files and trying to save them on the array or updating already loaded file.

//Add it to the DraggabelComponent 
...
addLoadedFile(file) {
    //SetState could take a callback which requires returning the state object to be applied
	this.setState((prevState) => ({
	  loadedFiles: [
	    ...prevState.loadedFiles,
	    file
	  ]
	}));
}
...

Adding a new file to the state of loaded files using the setState method which takes a callback that takes the previous state and returns the object to be applied to the state where we simply get the already loaded files from the array and we concatenate the new file to be added and the previous array using the ES6 Array spread Syntax to concatenate two arrays and elements.

...
removeLoadedFile(file) {
	//Remove file from the State
	this.setState((prevState) => {
	  let loadedFiles = prevState.loadedFiles;
	  //filter through all items but the one needs to be removed
	  let newLoadedFiles = _.filter(loadedFiles, (ldFile) => {
	    //return all elements but the file we want to remove (to get a new array without the target image file)
	    return ldFile != file;
	  });
      //Return loadedFiles to update the state 
	  return {loadedFiles: newLoadedFiles};
	});
}
//Remove all Files (Simply set the array to empty)
removeAllLoadedFile() {
  this.setState({loadedFiles: []});
}
...

To remove a signal file from the state we need to use Lodash filter method for filtering through files array and adding all elements to a new array but the one needs to be removed.

...
updateLoadedFile(oldFile, newFile) {
	this.setState((prevState) => {
      //Create a new instance since we need to mutate the array 
	  const loadedFiles = [...prevState.loadedFiles];
      //Find target file to be updated (oldFile)
	  _.find(loadedFiles, (file, idx) => {
	    if (file == oldFile) 
          //This is it! Update it with the new file object
	      loadedFiles[idx] = newFile;
	    }
	  );
      //Set State 
	  return {loadedFiles};
	});
    //Return new file instance so we could use later as oldFile if we ever wanted to update 
	return newFile;
}
...

Updating or changing oldFile with a NewFile object instance is quite simple, we look for the target file to be updated using Lodash (find) method which basically gives you a callback predict function with the current file and you could do whatever with it, so we change it by specifying it's index with the NewFile object.

after setting the state we return the NewFile instance so we could use it later for updating the same file (since the NewFile object becomes oldFile after updating it).

Lastly, we need to call the addNewFile method once the FileReader has successfully loaded the image and got it's base64 encoded data so we could add it to the files array on the state.

...
fileReader.onload = () => {
	console.log("IMAGE LOADED: ", fileReader.result);
	//File Instance for holding image data
    const file = {
	  data: fileReader.result,
	  isUploading: false
	}
	//Add file to the loadedFiles Array
	this.addLoadedFile(file);
}
...

Now once you drag and drop an image file simply browse for it and should be added to the array on the state.

Rendering Loaded Images

All of the management that is happening above is about to use the loadedFiles array on the state to render a preview of the current files to be uploaded to the server.

...
render() {
    //Extract loadedFiles from state 
    const {loadedFiles} = this.state;
    ...
	<div className="files-preview-container ip-scrollbar">
		{loadedFiles.map((file, idx) => {
		  return <div className="file" key={idx}>
		    <img src={file.data}/>
		    <div className="container">
		      <span className="progress-bar">
		        {file.isUploading && <ProgressBar/>}
		      </span>
		      <span className="remove-btn" onClick={() => this.removeLoadedFile(file)}>
		        <Icon icon={remove} size={19}/>
		      </span>
		    </div>
		  </div>
		})}
	</div>
    ...
}

For rendering, we loop through each element on the loadedFiles array and return a file div which holds the actual image from the file.data and a progress bar for showing upload progress bar as well as the remove cross button for allowing the user to remove a loaded image in case he doesn't want to upload it to the server.

The Progress bar is powered by the Blueprintjs progress bar component which is very simple and elegant all we have to do is check whether the file isUploading property is true or false for rendering it properly.

We use the React-icons-kit module for getting a (FontAwesome remove icon), the module has a large set of popular and well-known icons frameworks and libraries.

To remove a file we use the removeLoadedFile method on the remove-btn click event.

Uploading Images

Uploading the loaded images is all about sending the images array with AJAX to the backend server to handle and store the images properly on a database well, we can't cover the server side but still we can simulate the feature of a server with the setTimeout function to run a callback after a certain duration of time.

onUpload() {
	const {loadedFiles} = this.state;
    //Loop through loaded files and (AJAX Each one to the server)
	loadedFiles.map((file, idx) => {
      /*You should send AJAX request to the backend over here but we are just going to simulate the function of a backend server by using the setTimeout function*/
	  //Update file (Change it's state to uploading)
	  let newFile = this.updateLoadedFile(file, {
	    ...file,
	    isUploading: true
	  });
	  //Simulate a REAL WEB SERVER DOING IMAGE UPLOADING (3seconds)
	  setTimeout(() => {
	    //Get it back to it's original State
	    this.updateLoadedFile(newFile, {
	      ...newFile,
	      isUploading: false
	    });
	  }, 3000);

	});
}

For showing the progress bar we simply need to update the loaded file which currently on the loop by changing it's isUploading property to true and revert it back to false once the setTimeout has finished (just for server simulation).

It may be a little bit complicated depending on your Backend Server and what type of frameworks and languages you are using to receive the images data.

The final thing is we need to bind the onUpload method to the upload button on the rendering method.

render() {
  ...
  //Link method to the click event (make sure to bind the right class context)
  <AnchorButton
  text="Upload"
  intent={Intent.SUCCESS}
  onClick={this
  .onUpload
  .bind(this)}/>
  ...
}

Now just try to open up the index.html and test to upload some images.

 

What's Next

Of course, this is a very basic example of an ImageUploader on React with a simple drag and drop feature, you can add more feature and make it even more reliable with error handling and proper validation.

Check out Toasts Components from Blueprintjs framework which helps you to provide errors and success message in an elegant way to the user.

Share Tutorial

Made With By

Ipenywis Founder, Game/Web Developer, Love Play Games