Using React with Django, with a little help from Webpack

View the complete app, with different stages tagged, on Github

Hooking React into your Django app can be difficult, especially if you’re unfamiliar with webpack, npm, and babel.

I recommend you take the time to read the docs for all these tools, but to get you started I’m going to walk you through how to get Django Rest Framework (DRF) to work with React. This tutorial assumes you already have DRF set up (tag v0.0.1 in the Github repo). If you don’t know how to set up DRF, work through the tutorial.

Some of the webpack stuff in here is lifted almost directly from this tutorial by the author of django-webpack-loader. His tutorial hasn’t been updated for npm@3, though, and doesn’t go into making AJAX requests with React.

Here’s our roadmap:

Our Tools

Hello npm

Navigate to the root directory of your Django project and run npm init. Then follow the prompts to have npm automatically create a package.json file for you. If you’ve never used npm before, it’s like python’s virtualenv, except that it stores dependencies in a directory called node_modules.

Now we’re going to install our npm dependencies - babel, jquery, react, and webpack. Run the following command to install these dependencies in node_modules. The --save-dev flag tells npm to automatically add these dependencies to package.json.

npm install --save-dev jquery react react-dom webpack webpack-bundle-tracker babel-loader babel-core babel-preset-es2015 babel-preset-react

Don’t be alarmed if the above command takes a while to run. npm has huge dependency chains, meaning that it takes a lot of HTTP requests to get all the necessary data.

Hello Webpack

Now that we’ve installed our dependencies, it’s time to set up our module bundler, webpack. First, run mkdir -p assets/js to create a directory for our js code. Then run touch assets/js/index.js. This file will be the entry point of our app.

Create a file called webpack.config.js in your root directory. This will store configuration information that tells webpack what plugins to use, where to look for modules, how to compile different file extensions, etc. You can read about all the options this config file provides you in the webpack docs.

Below is how our webpack.config.js file is going to look. I recommend that you type it all out by hand, and figure out how each part works. This will make your life a lot easier when you want to come back and customize it later.

//require our dependencies
var path = require('path')
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')

module.exports = {
    //the base directory (absolute path) for resolving the entry option
    context: __dirname,
    //the entry point we created earlier. Note that './' means 
    //your current directory. You don't have to specify the extension  now,
    //because you will specify extensions later in the `resolve` section
    entry: './assets/js/index', 
    
    output: {
        //where you want your compiled bundle to be stored
        path: path.resolve('./assets/bundles/'), 
        //naming convention webpack should use for your files
        filename: '[name]-[hash].js', 
    },
    
    plugins: [
        //tells webpack where to store data about your bundles.
        new BundleTracker({filename: './webpack-stats.json'}), 
        //makes jQuery available in every module
        new webpack.ProvidePlugin({ 
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery' 
        })
    ],
    
    module: {
        loaders: [
            //a regexp that tells webpack use the following loaders on all 
            //.js and .jsx files
            {test: /\.jsx?$/, 
                //we definitely don't want babel to transpile all the files in 
                //node_modules. That would take a long time.
                exclude: /node_modules/, 
                //use the babel loader 
                loader: 'babel-loader', 
                query: {
                    //specify that we will be dealing with React code
                    presets: ['react'] 
                }
            }
        ]
    },
    
    resolve: {
        //tells webpack where to look for modules
        modulesDirectories: ['node_modules'],
        //extensions that should be used to resolve modules
        extensions: ['', '.js', '.jsx'] 
    }   
}

Webpack should be properly configured now.

Hello React

Let’s create a simple Hello World app in React, just to test that our bundler is compiling JSX properly. Open assets/js/index.js and enter the following code:

var React = require('react')
var ReactDOM = require('react-dom')

var Hello = React.createClass ({
    render: function() {
        return (
            <h1>
            Hello, React!
            </h1>
        )
    }
})

ReactDOM.render(<Hello />, document.getElementById('container'))

Now let’s run our bundler as a quick sanity check. Run ./node_modules/.bin/webpack --config webpack.config.js from the command line. If you’ve entered everything as explained above, you should see a message containing the name of your new bundle (main followed by a hash). Make sure you deal with any error messages before continuing.

Hello Django

First off, make sure you’re in your virtualenv, then run pip install django-webpack-loader. Make the following changes to your settings.py file.

INSTALLED_APPS = (
    ...
    'webpack_loader'
    ...
)

STATICFILES_DIRS = (
    #This lets Django's collectstatic store our bundles
    os.path.join(BASE_DIR, 'assets'), 
)

WEBPACK_LOADER = {
    'DEFAULT': {
        'BUNDLE_DIR_NAME': 'bundles/',
        'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
    }
}

Open up the html template where you want to include React. Add the template tag {% load render_bundle from webpack_loader %} at the very top of the page. Then include {% render_bundle ‘main’ %} in the body of your template, where you want your React code to run. Just make sure to add it below a div with the attribute id="container".

Run your localhost server and navigate to the page where you included React. You should see a header saying “Hello React!”

We are now at v0.0.2 in the GitHub repo

Hello AJAX

If you were paying attention, you’ll have noticed we included a jQuery plugin in our webpack.config.js file. It’s not necessary, since you could just as well create XHR requests with vanilla JavaScript, but it makes it a lot easier. You can now use jQuery to to populate your page with data from your API. Edit assets/js/index.js as follows:

var React = require('react')
var ReactDOM = require('react-dom')

var BooksList = React.createClass({
    loadBooksFromServer: function(){
        $.ajax({
            url: this.props.url,
            datatype: 'json',
            cache: false,
            success: function(data) {
                this.setState({data: data});
            }.bind(this)
        })
    },

    getInitialState: function() {
        return {data: []};
    },

    componentDidMount: function() {
        this.loadBooksFromServer();
        setInterval(this.loadBooksFromServer, 
                    this.props.pollInterval)
    }, 
    render: function() {
        if (this.state.data) {
            console.log('DATA!')
            var bookNodes = this.state.data.map(function(book){
                return <li> {book.title} </li>
            })
        }
        return (
            <div>
                <h1>Hello React!</h1>
                <ul>
                    {bookNodes}
                </ul>
            </div>
        )
    }
})

ReactDOM.render(<BooksList url='/api/' pollInterval={1000} />, 
    document.getElementById('container'))

Compile the bundle one more time, and then run your localhost server to see data from the page loaded asynchronously. Now, try adding new books to the database, and watch them appear on the page. Awesome!

Success

This tutorial has provided only a small taste of everything offered by DRF and React. But now you know how the two work together, and you can get on to the fun part of actually building stuff.