Skip to content

Instantly share code, notes, and snippets.

@genomics-geek
Last active September 9, 2021 23:11
Show Gist options
  • Select an option

  • Save genomics-geek/81c6880ca862d99574c6f84dec81acb0 to your computer and use it in GitHub Desktop.

Select an option

Save genomics-geek/81c6880ca862d99574c6f84dec81acb0 to your computer and use it in GitHub Desktop.
Setup ReactJS, Redux, Webpack with React Hot Reloading on an existing Django Project

Setting up ReactJS/Redux using Webpack for an existing Django project

This guide will help set up your django project to use ReactJS

1. Install Python dependencies

Add pip requirements to our django project:

  • django-webpack-loader==0.4.1 ( Connects Django project with Webpack)
  • django-cors-headers==2.0.2 (Allows us to easily customize CORS settings)

Add new dependencies to INSTALLED_APPS

INSTALLED_APPS = [
    ...
    'corsheaders',
    'webpack_loader',
    ...
]

Adjust settings for new apps

Add CORS settings in your base settings:

# CORS CONFIGURATION
# ------------------------------------------------------------------------------
# https://github.com/ottoyiu/django-cors-headers#configuration
CORS_ORIGIN_ALLOW_ALL = True

Add Webpack loader settings for local and production settings.

Local:

# Webpack Loader by Owais Lane
# ------------------------------------------------------------------------------
# https://github.com/owais/django-webpack-loader
WEBPACK_LOADER = {
    'DEFAULT': {
        'BUNDLE_DIR_NAME': 'builds-dev/',
        'STATS_FILE': os.path.join(str(ROOT_DIR), 'frontend', 'webpack', 'webpack-stats.dev.json')
    }
}

Production:

# Webpack Loader by Owais Lane
# ------------------------------------------------------------------------------
# https://github.com/owais/django-webpack-loader
WEBPACK_LOADER = {
    'DEFAULT': {
        'BUNDLE_DIR_NAME': 'builds/',
        'STATS_FILE': os.path.join(ROOT_DIR, 'frontend', 'webpack', 'webpack-stats.production.json')
    }
}

Add CORS middleware according to these instructions. I personally split them up like below. This allows us to meet the criteria of both CORS and Whitenoise :).

In Base settings:

# MIDDLEWARE CONFIGURATION
# ------------------------------------------------------------------------------
SECURITY_MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
]

# This is required to go first! See: https://github.com/ottoyiu/django-cors-headers#setup
CORS_MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
]

DJANGO_MIDDLEWARE = [
    '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',
]

MIDDLEWARE = SECURITY_MIDDLEWARE + CORS_MIDDLEWARE + DJANGO_MIDDLEWARE

In Production settings:

# Use Whitenoise to serve static files
# See: https://whitenoise.readthedocs.io/
WHITENOISE_MIDDLEWARE = ['whitenoise.middleware.WhiteNoiseMiddleware', ]
# CORS Needs to go first! See: https://github.com/ottoyiu/django-cors-headers#setup
MIDDLEWARE = SECURITY_MIDDLEWARE + CORS_MIDDLEWARE  + WHITENOISE_MIDDLEWARE + DJANGO_MIDDLEWARE

2. Adjust STATICFILES

This where our ReactJS project will live.

Create a frontend directory

mkdir -p frontend

Add new path to STATICFILES_DIRS

STATICFILES_DIRS = [
     str(APPS_DIR.path('static')),
     str(ROOT_DIR.path('frontend')),
]

3. Install Node dependencies

This will install all the Javascript libraries we will use

Setup Node

Run npm init and follow the instructions. Just fill in the information for your project.

Install webpack packages

These are webpack packages. To read more about Webpack visit here. For now, I am using Webpack v1 because Webpack v2 seems a bit unstable and has changed a bit.

npm install --save-dev webpack@1 webpack-dev-server@1 webpack-bundle-tracker

Install babel compiler and plugins

These are packages that allow us to write our code using new ES6 JS. To read more, visit here.

npm install --save-dev babel-cli babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-2 css-loader style-loader

Install additionaly helpful libs

  • axios - Helpful to interact with an API
  • lodash - Helpful extra methods that are missing in JS
  • victory - Amazing library for charting
npm install --save-dev axios lodash victory

Install ReactJS and Redux and associated plugins

  • ReactJS is a JS lib for building UIs
  • Redux is a predictable state container for JS apps

Main ReactJS and Redux:

npm install --save-dev react react-dom redux

ReactJS and Redux plugins:

NOTE: Sticking with react-router@3 and react-router-redux@4 because new versions are in beta and unstable.

npm install --save-dev prop-types react-bootstrap react-fontawesome react-router@3 react-router-redux@4 react-cookie redux-logger redux-thunk react-redux

To allow for hot reloading of React components:

npm install --save-dev react-hot-loader@next redux-devtools redux-devtools-dock-monitor redux-devtools-log-monitor

Install ESLint packages

I choose to use the airbnb JS standards, since they have clearly stated it here

npm install --save-dev eslint eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react eslint-config-airbnb babel-eslint

Install Unit Testing packages

  • Karma - Test runner
  • Mocha - Testing framework
  • expect - lets you write better assertions
npm install --save-dev karma mocha expect deepfreeze karma-mocha karma-webpack karma-sourcemap-loader karma-chrome-launcher karma-babel-preprocessor enzyme

4. Setup Webpack module bundler

This will help us bundle and compile all of our front-end stuff. To read more, visit here. In short, it bundles all JavaScript, JSX, etc. code for our project and manages our codebase to be split into bundles to be loaded in in our different environments.

Create our webpack dir

mkdir -p frontend/webpack/

Create our base webpack config

Create frontend/webpack/webpack.base.config.js. The contents should be:

module.exports = {

  module: {
    loaders: [{
      test: /\.(js|jsx)$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
      query: {
        presets: ['es2015', 'stage-2', 'react']
      }
    }, {
      test: /\.css$/,
      loader: 'style-loader!css-loader'
    }]
  },

  resolve: {
    modulesDirectories: ['node_modules'],
    extensions: ['', '.js', '.jsx']
  }
};

Create frontend/webpack/webpack.local.config.js. The contents should be:

var path = require('path');
var BundleTracker = require('webpack-bundle-tracker');
var webpack = require('webpack');
var config = require('./webpack.base.config.js');

config.entry = {
  main: [
    'webpack-dev-server/client?http://0.0.0.0:3000',
    'webpack/hot/only-dev-server',
    'react-hot-loader/patch',
    path.join(__dirname, '../js/src/main/index')
  ]
};

config.devtool = 'inline-sourcemap';
config.output = {
  path: path.join(__dirname, '../js/builds-dev/'),
  filename: '[name]-[hash].js',
  publicPath: 'http://0.0.0.0:3000/js/builds/',
};

config.plugins = [
  new webpack.HotModuleReplacementPlugin(),
  new BundleTracker({ filename: './frontend/webpack/webpack-stats.dev.json' }),
  new webpack.DefinePlugin({
    'process.env': {
      NODE_ENV: JSON.stringify('development'),
      BASE_URL: JSON.stringify('http://0.0.0.0:8000/'),
    }
  })
];

config.module.loaders[0].query.plugins = ['react-hot-loader/babel'];

config.devServer = {
  inline: true,
  progress: true,
  hot: true,
  historyApiFallback: true,
  host: '0.0.0.0',
  port: 3000
};

module.exports = config;

Create frontend/webpack/webpack.production.config.js. The contents should be:

var path = require('path');
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');
var config = require('./webpack.base.config.js');

config.entry = {
  main: [
    path.join(__dirname, '../js/src/main/index')
  ]
};

config.output = {
  path: path.join(__dirname, '../js/builds/'),
  filename: '[name]-[hash].min.js',
  publicPath: '/js/builds/'
};

config.plugins = [
  new BundleTracker({ filename: './frontend/webpack/webpack-stats.production.json' }),
  new webpack.optimize.DedupePlugin(),
  new webpack.optimize.OccurenceOrderPlugin(),
  new webpack.DefinePlugin({
    'process.env': {
      NODE_ENV: JSON.stringify('production'),
      BASE_URL: JSON.stringify('http://0.0.0.0/'),
    }
  }),
  new webpack.optimize.UglifyJsPlugin({
    mangle: false,
    sourcemap: false
  })
];

module.exports = config;

Create the entry point for our front-end project

mkdir -p frontend/js/src/main
mkdir -p frontend/builds
mkdir -p frontend/builds-dev

touch frontend/js/src/main/index.jsx
touch frontend/builds/.gitkeep
touch frontend/builds-dev/.gitkeep
@morinx
Copy link

morinx commented Apr 30, 2017

Hey Mike, thanks for this. Super handy.

I got the app setup and the router.jsx loads successfully. However, I'm getting an error when saving changes into it. The hot reloading throws this:

XMLHttpRequest cannot load http://0.0.0.0:3000/js/builds/d134a1fd8102c947545f.hot-update.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8000' is therefore not allowed access.
dev-server.js:22
[HMR] Update failed: Error: Manifest request to http://0.0.0.0:3000/js/builds/d134a1fd8102c947545f.hot-update.json timed out.
at XMLHttpRequest.request.onreadystatechange (http://0.0.0.0:3000/js/builds/main-d134a1fd8102c947545f.js:34:23)

I made sure CORS_ORIGIN_ALLOW_ALL = True , but it seems that it's not effective.
Have you run into this or any idea why this might happen?

@morinx
Copy link

morinx commented Apr 30, 2017

So the only option that has worked so far is to add headers: { 'Access-Control-Allow-Origin': '*' }, to config.devServer
I guess it seems reasonable since CORS_ORIGIN_ALLOW_ALL takes care of the first page reload only.

@genomics-geek
Copy link
Author

Hey @morinx,

I came across that issue. Yup you are right, I forgot to add headers to the devServer. I made that change in the docs

@genomics-geek
Copy link
Author

I am also going to take the time to migrate this to webpack 2. Following this: https://webpack.js.org/guides/migrating/.

Wish me luck :)

@ouki-kanki
Copy link

thanks so much for the effort to put this information all together.I was looking something like this for ages.

@genomics-geek
Copy link
Author

genomics-geek commented Jan 13, 2018

This has been deprecated.

You can use this instead. I am going to try to incorporate it into pydanny's cookiecutter, but if not you can get it from here :)

It includes:

  • Integration of Django with Facebooks create react app
  • React Hot Reloading

@genomics-geek
Copy link
Author

Recent update: Now integrated Django 2 with ReactJS using create-react-app without ejecting! So can leverage all the improvements/abstractions of create-react-app. I have recently switched all of my projects to this to avoid maintaining webpack configs, etc.

Can find it here

To use: cookiecutter https://github.com/chopdgd/cookiecutter-django-reactjs

@vaas-montenegro
Copy link

Recent update: Now integrated Django 2 with ReactJS using create-react-app without ejecting! So can leverage all the improvements/abstractions of create-react-app. I have recently switched all of my projects to this to avoid maintaining webpack configs, etc.

Can find it here

To use: cookiecutter https://github.com/chopdgd/cookiecutter-django-reactjs

Hi , how to use hot reload in react when using django cookicutter react

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment