Using ReactJS in Django

Becoming a full-stack web developer! heyylateef | Nov 30 2021



Overview

There is a lot of moving parts to web development, front end, back end, database, application server, etc. Many people over the years have coined terms such as MERN, MEAN, or even PDRP. These are common software stacks that web developers use for every project. In this tutorial, we'll be exploring the PDRP (PostgreSQL, Django, React, Python) stack to see how we can use React within our Django project. The goal of this tutorial is to create a todo list, with submitting the todos to  a back end API using the Django Rest Framework and displaying/consuming the todos with React.


What is React?

ReactJS is a front-end JavaScript framework developed and maintained by Facebook. It utilizes a web page's "virtual DOM" to improve performance of your web application. Typical use cases for ReactJS include:

  • Single Page Applications
  • Ecommerce websites
  • Front facing APIs



What is wrong with HTML5/Bootstrap?

There is a misconception that ReactJS simply will replace HTML5/Bootstrap. This isn't true as there are plenty of use cases where HTML5/Bootstrap is sufficient for your project such as:

  • Small projects/proof of concepts that require quick turnover 
  • Web developers of all experience levels (from beginners to professionals) understand HTML, making it easier to work in a team and get support
  • Arguably the simplest front-end language/tool to understand


Why use Django?

Django is a web framework that is "batteries included". It has all the necessary tools to create a scalable, secure web application. Since the framework gives us the freedom to self host our static files (thanks to Whitenoise), we can deploy our front end (React) inside of Django! Besides the ability to deploy React (React is just JavaScript, which are considered static files), here are some other reasons why I prefer to use Django for as my project's back-end:

  • Built in security against common web attacks such as SQL database injection, cross site scripting, etc.
  • Includes everything a web developer needs to successfully create a web application
  • Capability to scale up on application resources such as database servers, cache servers, application servers if developer chooses to scale



1. Set up your Django project (The REST API)

Lets start by creating a basic Django project. On your PC, create a folder called django-react. Open terminal/CMD on your PC, change the directory to the django-react folder and enter the following:

$ django-admin startproject backend


Now lets create the Django app that'll be our REST API. Inside the same directory that holds manage.py, run the following command in terminal: 

$ python manage.py startapp api

At this point, your file structure should look like the following:

django-react/
manage.py
backend/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
api/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py


Now lets get Django dependancies, Django Rest Framework and django-cors-headers. Inside the terminal/CMD, install those packages by running the following command:

$ pip install django-cors-headers djangorestframework




1A. Adding packages and configure middleware (Editing settings.py)

Open your Django settings file (settings.py). We're going to make a few changes in the settings file to install the rest framework, cors-headers, and our Django app (api). Inside "INSTALLED APPS", enter the following:

#django-react/backend/settings.py

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', #Django REST Framework
'corsheaders', #CORS Header
'api', # our Django app
]

Under "MIDDLEWARE", enter the following:

#django-react/backend/settings.py

MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # added to solve CORS
'django.middleware.common.CommonMiddleware', # added to solve CORS
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Lastly, we have to let our Django server know that it's safe to use resources from our React application. Lets add the following to the bottom of settings.py:

#django-react/backend/settings.py

CORS_ORIGIN_ALLOW_ALL = True # added to solve COR


1B. Connect our app urls to our project urls (Editing backend/urls.py)

As with every Django project, we need to configure the urls.py on the project level. We'll connect the urls.py on our Django app to the root path of our entire Django project. Open urls.py within the backend folder (backend/urls.py) and paste the following:

#django-react/backend/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('', include('api.urls')), #our apps' url.py
]


1C. Setting up our app's url routes (Creating api/urls.py)

We must create our app's urls.py file. Open the api folder and create a new file named "urls.py". Paste the following:

#django-react/api/urls.py

from django.urls import include, path
from rest_framework import routers
from . import views

router = routers.DefaultRouter()

#Declares API endpoint named "/apitodo" that displays the ReactTodoViewSet inside views.py;
#Gives us ability to do GET, POST, PUT, and DELETE (to delete, use pk, ex. "api/todo/1")
router.register(r'todo', views.ReactTodoViewSet)

# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
path('api/', include(router.urls)),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path("", views.index, name="index")
]



In the code above, we use the rest framework's built-in router method. Using this built-in method provides us a quick way to spin up a API on an endpoint ("todo") on a specific Django url route ("/api") and give us the ability to do GET, POST, PUT, and DELETE requests without configuring these methods manually. Please note that there are many ways to set up url endpoints to the REST API, but we're using this setup so we can have access to the authentication endpoint (using the authentication endpoint is beyond the scope of this tutorial, however I believe that is great start code for people who choose to build upon this tutorial).Later, we're going to setup our project's views.py.

1D. Creating models in api/models.py

Lets create our model. Our model will be a simple comment-like model, taking in a name and detail text fields. Open models.py and paste the following:

#django-react/api/models.py

from django.db import models

class ReactTodo(models.Model):
name = models.CharField(max_length=30)
detail = models.CharField(max_length=500)


1E. Creating api/serializers.py

In the REST framework, serializers are very similar to traditional Django Forms. However, there are some useful extra things happening under the hood that enables all the CRUD actions (GET, POST, PUT, DELETE) for our API. We need to create the serializers.py file in our Django app. Inside the api folder, create a file named serializers.py and paste the following:

#django-react/api/serializers.py

from rest_framework import serializers
from . models import *
class ReactTodoSerializer(serializers.ModelSerializer):
class Meta:
model = ReactTodo
fields = ['id', 'name', 'detail']


1F. Rendering the frontend (Editing api/views.py)
Now this is where things begin to come together. Lets open views.py in our Django app. We're going to setup a function-based view that'll render our React project (we'll create this next) and a class-based view that'll serialize/deserialize the data submitted to Django. Copy and paste the following in views.py:

#django-react/api/views.py

from django.shortcuts import render
from rest_framework import viewsets
from .serializers import ReactTodoSerializer
from . models import *
# Create your views here.

class ReactTodoViewSet(viewsets.ModelViewSet):
#declares a variable "queryset" that includes the "Dict" model
queryset = ReactTodo.objects.all().order_by('id')

#sends the "queryset" variable to the serializer class to
#convert the "Dict" model to JSON
serializer_class = ReactTodoSerializer


For now, we're done setting up a Django API. Lets start the Django project for the first time by running migrations, creating a superuser, and starting the server and look at the browsable API. Inside terminal/CMD, run the following commands:

$ python manange.py makemigrations
$ python manage.py migrate
$ python manage.py createsuperuser
$ python manage.py runserver
Go to localhost:8000/api. Enter your superuser credentials, and your page may look like this:


Lets make our first submission via the browsable API! Go to localhost:8000/api/todo and scroll down to the bottom, you'll see a section where you can enter a "Name" and "Detail". Enter some text in both of the fields and click the "Post" button. Congratulations, you just made your first POST request to your API. Great, lets create our React project!

2. Installing React

To use React,, we must install NodeJS on our system. Go to https://nodejs.org/en/ and install NodeJS on your system.

After installation is finished, we're going to create our React project within our django-react folder, so both our frontend and backend are together in a single folder. In terminal/CMD, please make sure you're in the root folder for the Django project (.../django-react/) and run the following command:

$ npx create-react-app frontend
$ cd frontend

Now your project folder structure should look similar to this:

django-react/
manage.py
db.sqlite3
api/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
serializers.py
tests.py
urls.py
views.py
backend/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
frontend/
node_modules/
public/
src/
package-lock.json
package.json
README.md


3. React code

Open the frontend React project, inside the src folder, open "App.js".

Before we get started, lets install some npm packages (Axios, React-Bootstrap) to improve our React project. We'll use Axios to make requests to our API (I prefer to use Axios as it has better security features compared to window.fetch()), and use React-Bootstrap to add Bootstrap styling to our components. Inside terminal/CMD while inside the frontend folder, run the following commands to install the node packages:

$ npm install axios react-bootstrap

In our React project, we're going to create two new React components (Post and InputForm) and will render both components in the parent component (App.js). In our frontend/src directory, create two new files, Post.js and InputForm.js.



3A. Post.js (Display data from API; GET Request)

All of our React components will be function-based and take advantage of React Hooks. Without going into detail, React Hooks were introduced to help simplify the reusability of stateful objects and and management of complex React components. For more information about React Hooks, take a look at the official documentation.

Now for some code, open Post.js, copy and paste the following:

import 'bootstrap/dist/css/bootstrap.min.css';
import { Table } from 'react-bootstrap';
import {React, useState, useEffect} from 'react';
import axios from 'axios';

const Post = () =>{
const [posts, setPosts] = useState([{
id: 1,
name: "",
detail: "",
}])
useEffect(() => {
axios.get("http://localhost:8000/api/todo")
.then(result => {
setPosts(result.data);
console.log(result.data);
}).catch(error => alert(error));
}, []);


return (
<Table striped bordered hover variant="dark">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
{posts.map((obj) =>(
<tr key={obj.id}>
<td>{obj.id}</td>
<td>{obj.name}</td>
<td>{obj.detail}</td>
</tr>
))}
</tbody>
</Table>
);
};
export default Post;


Lets breakdown this components into four parts: importing packages, defining our state object, making our GET request, and rendering HTML.

  1. The first four lines in this file is importing our node packages. React-Bootstrap gives us all the features we know and use in regular HTML Bootstrap, but are actual React components, allowing us to blend all the things we love about React with all the features we love about Bootstrap. Please take note that when using React-Bootstrap, you don't need to import everything you would need throughout project, but only what you need for the component. Otherwise, most IDEs/linters will give you warnings/errors. In our case, we only use the Table component.
  2. We start by defining our component with a state variable,"posts", using the React Hook, useState, as an array that maintains the values of three properties (the same two attributes we defined in our Django models, plus the auto generated "id" property). Next, we use the "useEffect" React Hook to manage our GET request. We're using Axios within the useEffect() React Hook, as the code within useEffect() Hook will execute only when the Post component is render in our browser. For our tutorial, this is a perfect way to make a GET request to our API as soon as the Post component is rendered on our browser.
  3.  We're specifically using Axios for our GET request as it has some built in security features such as protection for cross site scripting. Right after making a GET request to our API, we'll use our setPosts function that we defined using the useState Hook. Remember, the job of setPosts is solely to manage the values of our state object (posts). Lastly, we attempt to catch and potential errors when making a GET request and tell our code to send an alert to the browser if we experience any errors. Also, please note of the empty array after the closer of the axios.get() method. The empty array is actually store the value of the axios.get() method and stops the useEffect Hook from continuously making requests to our API.
  4. Displaying the data we got from the API. We'll use React-Bootstrap's Table component, along with some table headers and display our data. We use the .map() method to loop through the array of data we set in our posts state object.

Save the file.

3B. InputForm.js (Submit data to API; POST Request)

Open InputForm.js. Copy and paste the following:

import {React, useState} from 'react';
import axios from "axios";
import { Form, Button } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';


function InputForm() {
const [values, setValues] = useState({
name: "",
detail: "",
});

const handleNameInputChange = event => {
setValues((values) => ({
...values,
name:event.target.value,
}));
};
const handleDetailInputChange = event => {
setValues((values) => ({
...values,
detail:event.target.value,
}));
};

const handleSubmit = () => {
axios.post("http://localhost:8000/api/todo/", {
name: values.name,
detail: values.detail
})
.then(result => {
alert("Comment submitted to Django!");
setValues({
name: "",
detail: ""
})
}).catch(error => alert(error));
}

return (
<div>
<Form onSubmit={handleSubmit}>
<Form.Group className="mb-3">
<Form.Label>Your Name</Form.Label>
<Form.Control
id='user-name'
type='text'
placeholder='User Name'
name='name'
value={values.name}
onChange={handleNameInputChange}
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Comment</Form.Label>
<Form.Control
id='user-detail'
type='text'
placeholder='Type a comment'
name='detail'
value={values.detail}
onChange={handleDetailInputChange}
/>
</Form.Group>
<Button
className="mb-3"
variant="warning"
as="input"
type="submit"
value="Submit"
/>
</Form>
</div>
)
}

export default InputForm;


Lets breakdown this component, in four parts; the state object "values", rendering the HTML, handle functions, and  the POST request.

  1. Again we define our state variable, values, as an array that takes two properties, name and detail, and we set the default values as an empty string. Since we're using the useState react Hook, we also define our function that'll manage the value of "values", we called it "setValues".
  2. Lets talk about rendering the HTML. Analyzing the HTML before going into the handle functions would save many readers from confusion. Using the React-Bootstrap Form component, we create a responsive React/HTML form, define what happens when we submit the form, define the default value for each field using JSX, and let our React code know what to do when the value of the field changes using React's onChange method. We have two handle functions that manage the events for both of our fields.
  3. We have three handle functions; handleNameInputChange, handleDetailInputChange, and handleSubmit. Both handleNameInputChange and handleDetailInputChange do both the same thing, they set the value of whatever the user types in their respective field to either name or detail properties of the state object values. The handleSubmit function manages the form submission.
  4. Our handleSubmit function makes a POST request to our Django API using Axios. It takes the value of values state object (we set the state of "values" in part 3 of this breakdown), sends it to our backend, displays an alert on the browser, and uses setValues to reset the  value of our state object, thus resetting the form. Lastly, we attempt to catch any errors. Please note, it is very popular to use event.preventDefault() in many cases when handling a form submission. However, we wanted to keep the default action of auto refreshing the web page after submission, and our Post component will automatically make a GET request to our API and display our newly submitted data.


Great! Save the file, now go to App.js. Here we're going to render both of our components.

3C. App.js (Rendering both components)

Open App.js and paste the following:

import React from "react";
import {Container } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import InputForm from "./InputForm";
import Post from "./Post";

function App() {
return (
<Container>
<InputForm></InputForm>
<Post></Post>
</Container>
);
}
export default App;


All we're doing in App.js is rendering both of InputForm and Post components, and wrapping them in a React-Bootstrap Container. Now save the file. It is time to run both our Django API and React project. Open two terminal/CMD windows, in one window lets run our Django API (make sure you're in the same directory as manage.py:

$ python manage.py runserver

In the second window, run your React project (again, make sure you're in the frontend directory)

$ npm start

Your browser should have two new tabs, lets focus on the React tab (localhost:3000). You should see the submission you made earlier. Go ahead and make a new submission, this time from React.

4. Connecting our React project to Django templates and deploying both together

Wouldn't be great if we can deploy both React and Django together with a single command? Since Django has the capability to deploy static files on its own, we can deploy of React project inside Django (again, React is JavaScript, and JavaScript is considered static files). We have to do 3 things:

  1. Create the build version of our React project
  2. Change the static files directory in Django to look at our build folder for static files
  3. Render the React project within Django's views.py

4A. Create the React build folder

Stop both terminal/CMD windows, and run the following command within the terminal/CMD window you used to run the React project:

$ npm run build
Close the terminal/CMD window, we're finished with setting up our React project.

4B. Edit settings.py (Changing our static files directory to link to our build folder)

Inside of the Django settings file, we have to make two changes, inside the settings.py file, copy and paste the following, replacing the TEMPLATES section:

#django-react/backend/settings.py

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
#'DIRS': [],
#Sets path to our React app, named frontend.
'DIRS': [os.path.join(BASE_DIR, 'frontend')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]


Now move towards the bottom of the settings file, under STATIC_URL and paste the following:

#django-react/backend/settings.py

# update the STATICFILES_DIRS to look for React build folder
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "frontend", "build", "static"),
)


Great, we just linked our Django static files to our React build folder.

4C. Edit views.py (rendering our React project's index.html)

Lastly, lets render our React project. Open our Django app's views.py folder and add the following:

#django-react/backend/api/views.py

def index(request):
return render(request, "build/index.html") #Renders the React app


Save all files. Now go back to termainal/CMD, and start the Django API:

$ python manage.py runserver

Go to localhost:8000. Congratulations, you just deployed React within Django while also serving a REST API.

About The Lab

Like the content posted on this site? Need help with another web project? Want to give some feedback? Feel free to contact me via email or social media!

Know more!
DigitalOcean Referral Badge