How to Bring Live-Reloading Back to a Django And React Project

Written by Toruitas | Published 2020/08/13
Tech Story Tags: django | react | reactjs | frontend | front-end-development | python | python-web-development | javascript

TLDR How to Bring Live-Reloading Back to a Django And React Project is a prequel to my 110% Complete JWT Authentication with Django & React - 2020 article. I couldn't figure out how to get live reloading working in a typical React and Django Rest Framework project, and it does that well enough. So for every change made to the frontend, we had to shut down the. dev server, rebuild the React. bundle, and start it up again. Talk about tedious!via the TL;DR App

This article is a prequel of sorts to my earlier 110% Complete JWT Authentication with Django & React - 2020 article. That article covers how to get started with JWT authentication in a typical React and Django Rest Framework project, and it does that well enough. There's just one major annoyance with it - I couldn't figure out how to get live reloading working! So for every change made to the frontend, we had to shut down the dev server, rebuild the React bundle, and start it up again. Talk about tedious!
Recently I gave it another go as I developed a project which uses Celery and Django Channels for Websockets realtime interaction. That is a subject for another article.
Getting live reloading working is actually quite easy and straightforward, although it does require running both Django and Webpack dev servers simultaneously, so let's run through the code with a barebones setup.
If you just want the code, it's on GitHub here.
First, install Django and Django Rest Framework.
pipenv --python=3.8
pipenv shell
pipenv install django djangorestframework
Then create the project.
django-admin startproject djrhr
And the frontend app which will hold all the React code, and run all the standard migrations.
cd djrhr
python manage.py migrate
django-admin startapp frontend
cd ..
So now the structure of the project is like so:
djr-hotreload
--djrhr
----djrhr
----frontend
----manage.py
First thing we are going to do is prepare Django for serving the single
index.html
which will load the React script.
In
frontend/
, create a
templates/frontend
directory with an
index.html
and a
urls.py
, plus a related
style.css
like so:
frontend
--migrations
--templates
----frontend
------index.html
--static
----frontend
------style.css
--__init__.py
--admin.py
--apps.py
--models.py
--tests.py
--urls.py
--views.py
Let's prepare
index.html
for loading React.
<!DOCTYPE html>
<html>
{% load static %}
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="{% static 'frontend/style.css' %}">
    <title>Demo of Django and React hot-reloading</title>
</head>
<body>
    <div id="root" class="content">
        This will be the base template.
    </div>
<script type="text/javascript" src="{% static 'frontend/public/main.js' %}"></script>
</body>
</html>
We'll add the extra stuff to the static folder in a bit.
First, create a view to serve our
index.html
. Standard Django view.
# views.py
from django.shortcuts import render

# Create your views here.
def index_view(request):
    return render(request,'frontend/index.html', context=None)
Add it to
urls.py
from django.urls import path
from django.conf.urls import url
from .views import index_view

urlpatterns = [
    path('', index_view),
    url(r'^.*/$', index_view)  # regex matches, then lets routing be handled by the frontend. Still needs a / at end.
]
And of course add the app in
settings.py
and URLs to the project
urls.py
# urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('frontend.urls'))
]
# settings.py

...

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'frontend'
]

...
Now run that server and navigate to
http://127.0.0.1:8000/
You should see a nice, boring template with the text
"This will be the base template."
Cool.
That's pretty much all we need to do on the Django side of things.
Now for React!
In the frontend directory, make a
src
directory which will hold the React code we write. Then in frontend's
static
directory, make a
public
folder, which will hold the React code bundle for serving as represented by
main.js
in the tree below (you don't need to make this file).
frontend
...
--src
--static
----frontend
------style.css
------public
--------main.js
...
We will use WebPack to handle our frontend development server, so first let's install everything. Make sure you're at the top level and then first initialize your npm project, then install what we need:
npm init
npm install --save react react-dom
npm install --save-dev webpack webpack-cli webpack-dev-server
Now you should have a new
package.json
and
package-lock.json
.
Django doesn't pay much attention to the src folder we created, which will hold all the code we write fro our React project. So, any changes there are not seen and the Django dev server doesn't reload to show the improvements. To get around that, we need to utilize the Webpack dev server, which will see the changes, rebuild the bundle, and put it in the static folder. Django will see it there and respond appropriately.
To use the Webpack development server, add this line to the scripts section of
package.json
.
"start:dev": "webpack-dev-server --config webpack.config.js",
Which can be used with the terminal command
npm run start:dev
.
Also in
package.json
change
"main"
to
"webpack.config.js"
Before we can do that, we have to actually configure Webpack.
Install the proper loaders for React.
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader
Create this
webpack.config.js
file.
const path = require('path');

module.exports = {
    entry: [path.resolve(__dirname, 'djrhr/frontend/src/index.js')],
    // entry: {main: '/home/toruitas/Documents/GitHub/LollipopAI/django_backend/lollipopAI/frontend/src/index.js'},
    output: {
        // where compiled files go
        path: path.resolve(__dirname, "djrhr/frontend/static/frontend/public/"),

        // 127.0.0.1/static/frontend/public/ where files are served from
        publicPath: "/static/frontend/public/",
        filename: 'main.js',  // the same one we import in index.html
    },
    module: {
        // configuration regarding modules
        rules: [
            {
                // regex test for js and jsx files
                test: /\.(js|jsx|mjs)?$/,
                // don't look in any node_modules/ or bower_components/ folders
                exclude:  /(node_modules|bower_components)/,
                // for matching files, use the babel-loader
                use: {
                    loader: "babel-loader",
                    options: {presets: ["@babel/env", "@babel/preset-react"]}
                },
            },
        ],
    },
    devServer: {
        writeToDisk: true,
    }
};

The key points to note are the output section, specifies where to put the emitted bundle code, and the devServer section. Make sure that
writeToDisk
is
true
, otherwise the devServer will simply hold the changes in memory and Django will never see them and never reload.
While the webpack devServer can do a LOT more than this - it is after all a complete dev server - we only need it for this one job. We don't need it to serve any files, we don't need it to perform any compression, we only need it to watch our code and recompile the files in real-time.
That's really all there is to it, so let's make write some super simple React code to test it out. Both App.js and index.js live in
frontend/src
.
// App.js

import React, { Component} from "react";


class App extends Component {

    constructor(){
        super();
    }

    render(){
        return(
            <div>
                This is the React index.
            </div>
        )
    }

}

export default App;
// index.js

import React from 'react'
import {render} from 'react-dom'
import App from './App';

render((
    <App  />
), document.getElementById('root'));
To test, in one terminal run
python djrhr/manage.py runserver
and in another run
npm run start:dev
. You should see a new
main.js
in
frontend/static/frontend/dist
, and if you didn't you may need to make the directory. Opening up 127.0.0.1:8000 will show "This is the React index."
Go to
App.js
and change the line "This is the React index" to whatever you want. Maybe something like... "How much wood would a woodchuck chuck if a woodchuck could chuck wood?"
If everything has gone alright, then you can look in both terminals and see the code recompile, and as the browser refreshes the code reloads.
Success!
You can find the final project on GitHub code here.
And now that you've got this working, please do feel free to naturally navigate to my article 110% Complete JWT Authentication with Django & React if that's the kind of thing you need in your life.
Thanks for coding along with me! Follow me on Hackernoon @Toruitas
or on Twitter: @Stuart_Leitch as I work on stuff like this, machine
learning, philosophy in the digital age, and Creative Coding at my
startup
Lollipop.ai and at University of the Art London's Creative Computing Institute.

Written by Toruitas | Creative coder @ Lollipop.ai and UAL:Creative Computing Institute
Published by HackerNoon on 2020/08/13