PDF OCR via React, Django REST Framework, and Heroku, Part 3: The Front End

Joseph Cardenas
8 min readJul 30, 2020

File Upload via React

Now that we have a basic back end set up, let’s work on our React front end. To get things off the ground, the front end will be a simple form with a title, description, and upload button.

So, our first step will be to open a command line (Powershell for me) and create our React project. Navigate to your preferred project directory with (in my case) cd/Documents/Projects and create the React project with npx create-react-app tutorial_frontend . Make sure to refer to Part 1 if you need to install npm or React.

This is what your directory structure ought to look like starting off:

Front end tutorial directory structure

The public folder contains a basic web page — index.html — and other static files like logos. The src folder contains the key JavaScript (“JS”) of the project.

In your terminal, make sure you are in the tutorial_frontend directory and type npm start . Your browser will navigate to the localhost:3000 and display the following page:

The default React front page.

This is great, but we need a form to upload documents to our back end. The

Creating the Form

Right now our React app works, but it doesn’t do anything. To change this, we’ll need to add a form to upload our PDFs. The basic way to create forms in React is provided here, and this is the general way we’ll create our forms for this project.

Regular forms in HTML are created with the <form> ... </form> tags, and this is still the convention we’ll follow. That being said, we need to define some JS above in order to make sure the forms do what we intend when the user hits “Submit.”

Our first step is to replace the default boilerplate code in the App.js file. Remove the whole function App()block of code. This line is necessary in React whenever you are trying to create a new app. We will also change what we import. We will alter the line import React from ‘react’; to read import React, {Component} from ‘react’; . Why the change? React Components allow us to create individual, reusable user interface elements — read more here. We do not need to import the logo file. We can also remove the following:

import logo from './logo.svg';import './App.css';

We’ll need to install a module before we move on. From within your tutorial_frontend directory, type npm install axios, and that will install the module for us. In your App.js file, type import axios from 'axios'; underneath where you changed the line importing React.

We will also need to import the axios module; the axios library we import is meant to help us with connecting to the back end REST API we are going to create.

Now that we have mostly a blank slate to work with, we’ll create a new class called UploadForm. Enter the following code underneath your imports:

class UploadForm extends Component {    state = {        title: '',        content: '',        file: null    };

These fields ought to look familiar, as we defined similar variables when creating our backend.

At this point, the line at the very bottom our code, export default App; should be changed to export default UploadForm; now that we’ve changed the name of the app we’re creating.

Because this is the code for the interactive front end of our website, we need to track what the user changes the state of something. So, next we’ll add the following code that tracks when both values and files change in our front end. Note that we are still within the UploadForm code block, directly underneath the }; symbols ending the statearray:

handleChange = (e) => {    this.setState({    [e.target.id]: e.target.value    })};handleFileChange = (e) => {    this.setState({    file: e.target.files[0]    })};

Great, but how do we control the state when our users try and submit a PDF? The next block of code does precisely this, and will directly beneath what we entered above:

handleSubmit = (e) => {    e.preventDefault();    console.log(this.state);    let form_data = new FormData();    form_data.append('file', this.state.file, this.state.file.name);    form_data.append('title', this.state.title);    form_data.append('content', this.state.content);    let url = 'localhost:8000';    axios.post(url, form_data, {        headers: {        'content-type': 'multipart/form-data'        }      })        .then(res => {          console.log(res.data);        })        .catch(err => console.log(err))  };

Make sure everything lines up! This is, comparatively, a lot to take in. With the variable form_data, we are creating a variable to hold the actual form’s data we want sent. We now append the data from our file, title, and content fields to form_data. Then, we define a url variable and point it to our back end’s localhost:8000 url. Now, using the axios module we imported, we take both this url and our form_data, give it some headers to make it clear we’re sending form-data, and POST it!

We’re almost done, with just the rendering of the page needing to be finished. Enter the following block of text below right underneath the handleSubmit block but still nested within the UploadForm component:

render() {    return (      <div className="App">        <form onSubmit={this.handleSubmit}>          <p>            <input type="text" placeholder='Title' id='title' value={this.state.title} onChange={this.handleChange} required/>          </p>          <p>            <input type="text" placeholder='Content' id='content' value={this.state.content} onChange={this.handleChange} required/>          </p>          <p>          <input type="file"                 id="image"                 accept=".pdf, .doc, .txt "  onChange={this.handleFileChange} required/>          </p>          <input type="submit"/>       </form>      </div>    );  }

Now our app ought to compile when we run npm start from with the tutorial_frontend directory. A new web page should open to show two forms for Title and Content, a button to browse files to upload, and a “Submit Query” button.

Forms for typing our Title and Content, a button to browse files to upload and a Submit button

Nice! This is what your whole UploadForm.js file ought to look like in total:

import React, {Component} from 'react';import axios from 'axios';class UploadForm extends Component {  state = {    title: '',    content: '',    file: null  };  handleChange = (e) => {    this.setState({    [e.target.id]: e.target.value    })  };  handleFileChange = (e) => {    this.setState({    file: e.target.files[0]    })  };  handleSubmit = (e) => {    e.preventDefault();    console.log(this.state);    let form_data = new FormData();    form_data.append('file', this.state.file, this.state.file.name);    form_data.append('title', this.state.title);    form_data.append('content', this.state.content);    let url = 'localhost:8000';    axios.post(url, form_data, {      headers: {      'content-type': 'multipart/form-data'      }    })    .then(res => {    console.log(res.data);    })    .catch(err => console.log(err))  };  render() {    return (    <div className="App">    <form onSubmit={this.handleSubmit}>    <p>      <input type="text" placeholder='Title' id='title' value=    {this.state.title} onChange={this.handleChange} required/>    </p>    <p>      <input type="text" placeholder='Content' id='content' value={this.state.content} onChange={this.handleChange} required/>    </p>    <p>      <input type="file"             id="image"             accept=".pdf, .doc, .txt "  onChange={this.handleFileChange} required/>    </p>    <input type="submit"/>    </form>    </div>    );  }}export default UploadForm;

Now like with our back end project, we ought to create a tutorial_frontend repository in GitHub. If you need help remember the steps, refer back to where we did this in the back end tutorial. After the introductory push is done I’ll have us make a new branch. Type git checkout -b create-upload-form, do a git add . and git commit (remember to type any letter to enter vim’s editing mode and type your ESC key, then :wq to escape vim!) and finally a git push.

Connecting to our Django back end

Now that we have forms to submit data, let’s make sure we can send that data to our back end which will ultimately do the OCR.

Some Housekeeping

Going over the steps to make our front and back end talk to each other, I realized I had skipped a few steps. Without these steps, our front end might start up but won’t connect to the back end.

First, I forgot to register the model we made in our ocr/models.py file. We now need to open up our ocr/admin.py file and import our Post model by typing from .models import Post and then below typing admin.site.register(Post) . We won’t need to run makemigration or migrate after this, as we’ve not changed any of the models themselves.

Another critical thing we need to change is our settings.py file, specifically adding the line CORS_ORIGIN_ALLOW_ALL = True . The latter is a security setting that will allow our front end to communicate with the back end without getting blocked. I went ahead and created a #Security section in our settings.py, as we will update this later when we are ready to deploy to Heroku.

Now, make a new branch and post these changes to GitHub. After creating your pull request and merging your changes, switch back to main and do a git pull to ensure your main branch is up to date.

Upload via our React front end

Now that we know that our application works, we are ready to upload a file via React. Start up your React application and your Django application. Both your front end fields should appear and your Django REST API page ought to appear as normal. One way to make sure an application is properly sending data to another is to track activity across the network. To do this in your browser, we’ll use your browser’s Network Monitor tool.

Monitoring Your Network

Every modern browser has a set of web development tools built in, and one of those tools is a network monitor. I’ll leave it as an exercise for the reader on how to access those tools, but for now I’ll be using Firefox. I’ll go to my React page and open up the browser tools, then go to the network monitoring tab on my React page. I’ll reload the page and should see columns labeled Status, Method, Domain, File, etc:

In the Network tab, the network Status, Method used, Domain, and file sent should appear and indicate a successful upload.
The browser tools sub window opened on the Network tab.

We are looking specifically at the Status column, as this will tell us whether our upload was successful. We want the Status to be 201, with a POST method. As you did before in Postman, fill out the Title and Content fields, browse for a file to attach, and hit Submit Query. You should see the 201 status via the POST method. When you go to your back end REST API, you should see your new post!

You should see all the tests you’ve made via Postman and your new React frontend.

This is huge work, but we’re not done. Now that we’ve confirmed our apps work together, we need to deploy them via an online host — Heroku, in this case. In the next section we’ll cover how to prep your apps for deployment and what it takes to get them working together while deployed.

--

--