
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.
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}Within the component's structure, there are two state values:
picture: A reference to the picture file selected by the usersrc: The value of the source URL for our picture previewBoth values are initially set to false when the component is created.
The rendered content is an interface with three major sections:
1<input type="file"/><p> element.1<p>
2 No Preview
3</p>1<button>
2 Upload
3</button>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.
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}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];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);Finally update the state of your component using this.setState().
1this.setState({
2 picture: picture,
3 src: src
4});inputBind the function to the file input's onChange attribute:
1<input
2 type="file"
3 onChange={this.handlePictureSelected.bind(this)}
4/>Attaching .bind(this) will allow this to still retain its value, referring to the instance of the component even within the handlePictureSelected method.
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}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>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}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:
"file": A string representing the parameter key to be processed by the server.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 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>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.
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}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.