yoora
yoora

Reputation: 65

react django rendering same content

I'm working on Django-react project and having issue with rendering React component on Django server. To be precise, React always render me the same content even if I change it. I'm using Webpack, Babel-loader and running it on localhost.

project/templet/frontend/index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">   
  <title>Django-React</title>
</head>
<body>

    <div id="app" class="columns"><!-- React --></div>
	{% load static %}
	<script  type='text/javascript' src="{% static "frontend/main.js" %}"></script>
</body>

</html>

Entry point:

import ReactDOM from "react-dom";
import App from './components/App';

ReactDOM.render(<App/>, document.getElementById("app"));

Scripts in package.json:

  "scripts": {
    "dev": "webpack --mode development ./frontend/src/index.js --watch ./frontend/static/frontend/main.js",
    "build": "webpack --mode production ./frontend/src/index.js --output ./frontend/static/frontend/main.js"
  }

Babel config:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};

So, when I write some content in App component (eg. render div with "test") I can see it in my browser, but when I want to change it, and after refresing page, get the same content from div tag

Upvotes: 1

Views: 766

Answers (1)

OBAA
OBAA

Reputation: 424

From what i can understand from your question, When you first render a div with 'test' in it, it renders, but after that, further changes dont update.

Its because the javascript is not being updated on Django and you need to use collectstatic to sync the builds which is not very efficient.

The way to go is to use django-webpack-loader and webpack-bundle-tracker.

install webpack-bundle-tracker

npm install --save-dev webpack-bundle-tracker

install django-webpack-loader:

pip install django-webpack-loader

django-webpack-loader is a python package which injects link and script tag for the bundles which webpack generates dynamically.

webpack-bundle-tracker plugin emits necessary information about webpack compilation process to a json file so django-webpack-loader can consume it.

For webpack to track changes made in your App, You need to create a server that monitors changes in your React app and bundle your JS.

Note: We make use of a node server.

// Webpack Server

const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const config = require('./webpack.server.config');

new WebpackDevServer(webpack(config), {
  publicPath: config.output.publicPath,
  hot: true,
  inline: true,
  historyApiFallback: true
}).listen(3000, '0.0.0.0', function (err, result) {
  if (err) {
    console.log(err)
  }

  console.log('Listening at 0.0.0.0:3000')
});

The config files used by webpack.server.js would be at webpack.server.config.js

// webpack.server.config.js

const path = require("path");
const BundleTracker = require('webpack-bundle-tracker');

module.exports = {
    mode: 'development',

    entry: {
        main: './frontend/src/index.js',
        devServer: 'webpack-dev-server/client?http://localhost:3000'
    },

    output: {
        filename: "[name].js",
        path: path.resolve('./frontend/static/frontend/bundles/'),
        publicPath: 'http://localhost:3000/frontend/static/frontend/bundles/', // django-webpack-loader overrides django's STATIC_URL with this path
    },

    plugins: [
        new BundleTracker({filename: './frontend/webpack-stats.json'}), // Pass the correct path to the WEBPACK_LOADER in django settings
    ],

    devServer: {
        contentBase: './frontend/static/frontend/bundles/',
    },
};

Note that the server will by default keep the bundles in memory and not write to disk

When the server is stopped, there would be no sign of the bundled files as they were not compiled to memory.

To build your files to memory in development, create another config file at webpack.dev.config.js

// webpack.dev.config.js

const path = require("path");
const BundleTracker = require('webpack-bundle-tracker');

module.exports = {
    mode: 'development',

    entry: {
        main: './frontend/src/index.js',
    },

    output: {
        filename: "[name].js",
        path: path.resolve('./frontend/static/frontend/bundles/'),
    },

    plugins: [
        new BundleTracker({filename: './frontend/webpack-stats.json'}), // Pass the correct path to the WEBPACK_LOADER in django settings
    ],

};

While to build your files for Production, create another config file at webpack.prod.config.js

// webpack.prod.config.js

const path = require("path");
const BundleTracker = require('webpack-bundle-tracker');

module.exports = {
    mode: 'production',

    entry: {
        main: './frontend/src/index.js',
    },

    output: {
        filename: "[name].js",
        path: path.resolve('./frontend/static/frontend/dist/'),
    },

    plugins: [
        new BundleTracker({filename: './frontend/webpack-stats-prod.json'}), // Pass the correct path to the WEBPACK_LOADER in django settings
    ],

};

In your Django settings;

import sys
import os

# Assuming this is your base directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Also assuming this is your base directory
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'), 

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

# In production
if not DEBUG:
    WEBPACK_LOADER['DEFAULT'].update({
        'BUNDLE_DIR_NAME': 'dist/',
        'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats-prod.json') # Path to 'webpack-stats-prod.json'
    })

INSTALLED_APPS = (
 ...
 'webpack_loader',
)

Your index.html should look like this now;

{% load render_bundle from webpack_loader %}  <<< Add this.
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">   
  <title>Django-React</title>
</head>
<body>

    <div id="app" class="columns">
        <!-- React -->
    </div>

    {% render_bundle 'main' %}  <<< Add this.

</body>

</html>

Now, Your package.json should look like this;

"scripts": {
    "dev": "node ./webpack.server.js",
    "build-dev": "webpack --mode development --config ./webpack.dev.config.js",
    "build-prod": "webpack --mode production --config ./webpack.prod.config.js"
  }

So, To develop with automatic bundling, just run:

npm run dev

To build your files to memory after the webpack server is stopped, just run:

npm run build-dev

And finally, to build with production optimizations, just run:

npm run build-prod

I tried to modify to your project, feel to adjust according to your project structure. Check the below referrences to guide you better. They helped me out!

References:

  1. Let's modernize the way we handle frontend code with Django
  2. Using Webpack transparently with Django + hot reloading React components as a bonus
  3. Modern Django: Part 1: Setting up Django and React

Upvotes: 1

Related Questions