Displaying a preview of an image upload in React

Egor Egorov
3 min readFeb 17, 2018

--

Show your user a preview of the photo they want to upload

The button below should look familiar. We use it all the time to upload photos, add attachments to emails, submit homework, etc.

I decided to make a React component that lets you choose an image file, and display the preview of it. My plan is to do user authentication next, and then add the upload functionality to let people store and access their images in a database. But this article will be focusing only on the simple image preview. Most tutorials tell you to use fileReader() but it looked too complicated for such a simple functionality. Instead I found a really simple way to do it. Here is the whole code:

const React = require('react')class Upload extends React.Component {
constructor(props){
super(props)
this.state = {
file: null
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({
file: URL.createObjectURL(event.target.files[0])
})
}
render() {
return (
<div>
<input type="file" onChange={this.handleChange}/>
<img src={this.state.file}/>
</div>
);
}
}
module.exports = Upload

This is a very typical React component, with the only strange line being the one in bold. What’s going on there? Lets investigate.

According to the File Web API documentation, the html element <input/ type="file"> allows the user to select a local file and add it to the FileList object which stores the selected file to a files array, similarly to how React stores properties to this.state. To access the file we would do something like this:

//hmtl
<input id="fileItem" type="file">
//js
var file = document.getElementById('fileItem').files[0];

Handling events

In React, we don’t use document.getElementById() or anything like that to access the DOM. Instead we use component’s state to store and access relevant data. In this case use the onChange event, which normally would look like this in HTML:

<input onchange="handleChange(event)">

In JSX:

<input onChange={ (event) => this.handleChange(event) }> 

However, we want to avoid this way of writing JSX because it causes the creation of a new callback function every time the the input re-renders. This is usually not a problem, but it can have performance implications if the callback is passed to a lower level component.

The common approach in React is to explicitly bind the function in the component’s constructor like this:

this.handleChange = this.handleChange.bind(this)

Now you can reference it properly in your JSX like this:

<input onChange={ this.handleChange }>

But what is our function doing? Let’s get back to that strange line we use to store our file.

Every time there is a onChange event, our function is called with that event passed in as an argument. The event has some properties we can access with event.propertyName. The property we want is called target and it gives us the DOM element where the function was called from, which is our input element.

Now remember that our input can store a file to FileList file array, which we can access with the with the files property. here’s what it looks like:

event.target.files[0] //our file

objectURL ?

The URL.creatObjectURL() method takes an object (like our file) and creates a temporary local URL that is tied to the document in which it is created (meaning it won’t remember the URL if you leave the page and come back). This URL can be used to set the the src property of a <img/> element. But before we do that, we want to set this URL in the component’s state using setState() because it causes the state changes to be re-rendered to the DOM. That way as soon as a new image file is selected, it gets displayed immediately.

Now that we store the image URL every time it changes, we always have access to it with this.state.file.

That’s it! Next time we will add authentication using Firebase, and then make the upload functionality so users can store and access their photos.

--

--