Important Update
The Guide Feature will be discontinued after December 15th, 2023. Until then, you can continue to access and refer to the existing guides.
Author avatar

Raphael Alampay

Using React.js and jQuery to Update a Profile Picture with a Preview

Raphael Alampay

  • Sep 21, 2020
  • 9 Min read
  • 20,237 Views
  • Sep 21, 2020
  • 9 Min read
  • 20,237 Views
Web Development
Client-side Frameworks
React
Front End Web Development

Introduction

Developing an interface for a web app to allow users to upload a picture often means supplying an input element of type file as well as a section to preview the picture that the user selected from their computer. This guide will go over how to implement a simple React.js component that allows a user to select a picture, preview the picture, and handle the picture itself for submitting via an AJAX call.

Setup

First, create a component that maintains two states: one for the picture itself and another for the source URL for the preview. Next, you'll need the component to render an interface that contains an input element for file selection, a section for the picture preview, and a button to trigger the upload functionality. The PictureUploader component will initially look like the following:

1import React from 'react';
2import $ from 'jquery';
3
4export default class PictureUploader extends React.Component {
5  constructor(props) {
6    super(props);
7
8    this.state = {
9      picture: false,
10      src: false
11    }
12  }
13
14  render() {
15    return (
16      <div>
17        <h5>Picture Uploader</h5>
18
19        <input
20          type="file"
21        />
22        <br/>
23        <div>
24          <p>
25            No Preview
26          </p>
27        </div>
28        <hr/>
29        <button>
30          Upload
31        </button>
32      </div>
33    );
34  }
35}
javascript

States to Maintain

Within the component's structure, there are two state values:

  • picture: A reference to the picture file selected by the user
  • src: The value of the source URL for our picture preview

Both values are initially set to false when the component is created.

User Interface

The rendered content is an interface with three major sections:

  1. An interface to allow the user to select a picture from their computer
1<input type="file"/>
html
  1. A preview section for when the user selects the picture. For now, it will say "No Preview" within a <p> element.
1<p>
2  No Preview
3</p>
html
  1. A button for triggering the upload
1<button>
2  Upload
3</button>
html

Picture File Selection

Next, program an event handler that will process the picture whenever the user selects a picture to upload. Afterwards, create another function to deal with the preview of the picture. If no picture is selected, return "No Preview" text. Otherwise, return an img HTML tag containing the URL of the selected picture.

Event Handler Function

The event handler will extract the file picture and create a URL for the preview. Copy the following handler code to your component:

1handlePictureSelected(event) {
2  var picture = event.target.files[0];
3  var src     = URL.createObjectURL(picture);
4
5  this.setState({
6    picture: picture,
7    src: src
8  });
9}
javascript

This method accepts an event argument whose property target represents the input element this handler is bound to. In this case, your input element of type file is expected to have a property files, which is of data type array. Therefore, to get the file that the user selected, reference the first element of the array:

1var picture = event.target.files[0];
javascript

The src variable represents the URL for the picture preview. You can create this by calling URL.createObjectURL(), which accepts a file object. This creates a temporary URL attached to the global document variable. This means that if the page is refreshed, you'll lose that URL reference. In this case, the file object is picture.

1var src = URL.createObjectURL(picture);
javascript

Finally update the state of your component using this.setState().

1this.setState({
2  picture: picture,
3  src: src
4});
javascript

Binding to input

Bind the function to the file input's onChange attribute:

1<input
2  type="file"
3  onChange={this.handlePictureSelected.bind(this)}
4/>
jsx

Attaching .bind(this) will allow this to still retain its value, referring to the instance of the component even within the handlePictureSelected method.

Render Preview

Our preview section is a result of another function that contains an if/else statement. It checks the component's state src if it is not false, indicating that you have a value. If so, return JSX code that contains an img whose source is src. Otherwise, return JSX code that contains the text "No Preview."

1renderPreview() {
2  if(this.state.src) {
3    return (
4      <img src={this.state.src}/>
5    );
6  } else {
7    return (
8      <p>
9        No Preview
10      </p>
11    );
12  }
13}
javascript

Invoke this method within the render() method of the component, specifically as a replacement for the "No Preview" text you had initially.

1<div>
2{this.renderPreview()}
3</div>
jsx

Trigger Upload

Event Handler Function

Create a method called the upload method that contains an ajax() call from jQuery to perform the upload. Copy the following code to your component:

1upload() {
2  var formData = new FormData();
3
4  formData.append("file", this.state.picture);
5
6  $.ajax({
7    url: "/some/api/endpoint",
8    method: "POST",
9    data: formData,
10    cache: false,
11    contentType: false,
12    processData: false,
13    success: function(response) {
14      // Code to handle a succesful upload
15    }
16  });
17}
javascript

Notice that instead of passing a plain old JavaScript object, you instead create an instance of FormData to allow you to embed the picture object. Do so by invoking append from formData, which accepts two arguments:

  1. "file": A string representing the parameter key to be processed by the server.
  2. this.state.picture: The picture object itself extracted from state.
1var formData = new FormData();
2formData.append("file", this.state.picture);

Since you're passing FormData as data in your AJAX POST call, you'll have to set cache, contentType, and processData to false. For the url, substitute "/some/api/endpoint" with the actual endpoint where you would upload your picture.

Bind to Button

Bind the event handler for uploading to your button element's onClick attribute:

1  <button
2    onClick={this.upload.bind(this)}
3  >
4    Upload
5  </button>
javascript

Similar to your input binding, attaching .bind(this) will allow this to still refer to the instance of the component even within the upload handler.

Overall Code

The final code will look like this:

1import React from 'react';
2import $ from 'jquery';
3
4export default class PictureUploader extends React.Component {
5  constructor(props) {
6    super(props);
7
8    this.state = {
9      picture: false,
10      src: false
11    }
12  }
13
14  handlePictureSelected(event) {
15    var picture = event.target.files[0];
16    var src     = URL.createObjectURL(picture);
17
18    this.setState({
19      picture: picture,
20      src: src
21    });
22  }
23
24  renderPreview() {
25    if(this.state.src) {
26      return (
27        <img src={this.state.src}/>
28      );
29    } else {
30      return (
31        <p>
32          No Preview
33        </p>
34      );
35    }
36  }
37
38  upload() {
39    var formData = new FormData();
40
41    formData.append("file", this.state.picture);
42
43    $.ajax({
44      url: "/some/api/endpoint",
45      method: "POST",
46      data: formData,
47      cache: false,
48      contentType: false,
49      processData: false,
50      success: function(response) {
51        // Code to handle a succesful upload
52      }
53    });
54  }
55
56  render() {
57    return (
58      <div>
59        <h5>Picture Uploader</h5>
60
61        <input
62          type="file"
63          onChange={this.handlePictureSelected.bind(this)}
64        />
65        <br/>
66        <div>
67        {this.renderPreview()}
68        </div>
69        <hr/>
70        <button
71          onClick={this.upload.bind(this)}
72        >
73          Upload
74        </button>
75      </div>
76    );
77  }
78}
javascript

Conclusion

The process to create a component that can process picture files uploaded by the user from their computer is the same as any other React.js-based event handling. Bind a function to the input element and extract the target's value. In this case, your value is a picture file which you can maintain with the component's state for later integration with the actual upload logic. As a challenge and next step, try to modify the upload function code and see if you can integrate it with your app's picture upload API or a third party API endpoint that accepts picture uploads such as imgur.