code English post

Django and Modern JS Libraries – React8 min read

Temmuz 22, 2020 7 min read

Django and Modern JS Libraries – React8 min read

Reading Time: 7 minutes

In the previous part, we built a Django backend and GraphQL API that is responsible for the communication of the Django project and react app. In this part of the tutorial, we will create a single page application with React from scratch.

We will bundle our application with webpack and we will not use create-react-app boilerplate

Create React App from Scratch

Step – 1: Configuring development environment

(Note: if you already installed the node, you can skip this part)

We will use Node backend for the development environment.

Therefore we need to install Node and Node package manager npm. In order to prevent possible dependency problems, we will create a clean node environment.

I will use NVM which is a Node version manager and it allows us to create isolated Node environments.

In your terminal, run the code below.

# install node version managerwget -qO- <https:"" nvm-sh="" nvm="" v0.35.3=""""> | bash </https:>
Code language: HTML, XML (xml)
# check installation command -v nvm # should prints nvm, if it doesn"t # you can restart your terminal
Code language: PHP (php)
# install node # node" is an alias for the latest version nvm install node # use the installed version nvm use node # prints Now using node v..
Code language: PHP (php)

Now we can create a frontend directory in the Django project.

Go to the root directory of the project. ‘backend/’

In your terminal copy and paste the code.

# create frontend directory mkdir FRONTEND cd FRONTEND #in backend/FRONTEND create a Node project npm init # you may fill the rest
Code language: PHP (php)

Now we can install Javascript dependencies such as React and API related to other libraries.

# add core react library npm install react react-dom react-router-dom # add graphql client-side framework of Apollo and parser npm install apollo-boost @apollo/react-hooks graphql graphql-tag # -- DEVELOPMENT PACKAGES--- # add babel transpiler npm install --save-dev @babel/core @babel/preset-env @babel/preset-react # add webpack bundler npm install --save-dev webpack webpack-cli webpack-dev-server # add webpack loaders and plugins npm install --save-dev babel-loader css-loader style-loader html-webpack-plugin mini-css-extract-plugin postcss-loader postcss-preset-env
Code language: PHP (php)

If everything goes well, we can create necessary files.

# create source folder for client side code mkdir src # our react app's root file touch index.js #create webpack config file touch webpack.config.js # get into src folder cd src # create HTML entrypoint for React development touch index.html
Code language: PHP (php)
# our app file and styling touch App.js // Screens touch MovieList.js touch MoviePage.js # for styling touch App.css
Code language: PHP (php)

All npm packages contain a file that holds metadata about the app. This file is a package.json file.

You should update the package.json file.

Edit the scripts section and add Babel presets and postcss configurations.

{ "scripts": { "start": "webpack-dev-server --open --hot --mode development", "build": "webpack --mode production" }, "babel": { "presets": [ "@babel/preset-env", "@babel/preset-react" ] }, "postcss": { "plugins": { "postcss-preset-env": {} } }, }
Code language: JSON / JSON with Comments (json)

Step 2 – Webpack configuration and index.html file

What is webpack ?

Webpack is a module bundler and a task runner.

We will bundle all our JavaScript applications including CSS styling into two JavaScript files, if you prefer you can output only one file.

Because of the rich plugins, you can also do many things with Webpack like compressing with different algorithms, eliminate unused CSS code, extracting your CSS to different files, uploading your bundle to cloud providers like S3, etc…

I made two different Webpack settings in one file.

One is for the development environment, and the other one is for a production environment.

Also, note that we do not optimize these configurations.

Copy/Paste the following code into webpack.config.js file.

const path = require("path"); const HtmlWebPackPlugin = require("html-webpack-plugin"); // checks if it is production bundling or development bundling const isEnvProduction = process.argv.includes("production") // our JS root file const entrypoint = './index.js' const productionSettings = { mode: "production", entry: entrypoint, output: { // output directory will be the root directory of django path: path.resolve(__dirname, '../'), // this is the bundled code we wrote filename: 'static/js/[name].js', // this is the bundled library code chunkFilename: 'static/js/[name].chunk.js' }, optimization: { minimize: true, splitChunks: { chunks: 'all', name: true, }, runtimeChunk: false, }, devServer: { historyApiFallback: true, stats: 'normal', }, module: { rules: [ { loader: 'babel-loader', test: /\.js$|jsx/, exclude: /node_modules/}, { test: /\\.css$/i, use: [ // IMPORTANT => don't forget `injectType` option // in some cases some styles can be missing due to // inline styling. { loader: 'style-loader', options: { injectType: 'styleTag' } }, "css-loader" ], }, ] }, plugins: [ new HtmlWebPackPlugin({ // ENTRYPOINT - this is where webpack read our app for bundling template: "./src/index.html", // OUTPUT FILE // this is emitted bundle html file // ---------------------------------- // django will use this as template after bundling // ----------------------------------- filename:"./templates/index.html" }), ] }; const devSettings = { mode: "development", entry: entrypoint, output: { path: path.resolve(__dirname, './build'), publicPath: "/", filename: 'static/js/bundle.js', chunkFilename: 'static/js/[name].chunk.js', }, devtool: 'inline', devServer: { historyApiFallback: true, contentBase: './dist', stats: 'minimal', }, module: { rules: [ { loader: 'babel-loader', test: /\.js$|jsx/, exclude: /node_modules/}, { test: /\.css$/i, use: [ //{loader: MiniCssExtractPlugin.loader, options: { // //your styles extracted in a file for production builds. // //hmr: isEnvDevelopment, // }, // }, // IMPORTANT => don't forget `injectType` option { loader: 'style-loader', options: { injectType: 'styleTag' } }, "postcss-loader" //"css-loader" //{ loader: 'sass-loader' }, ], }, ] }, plugins: [ new HtmlWebPackPlugin({ template: "./src/index.html", }) ] }; module.exports = isEnvProduction ? productionSettings : devSettings;
Code language: JavaScript (javascript)

When we are developing frontend, our React app renders all our JavaScript code to this HTML file in the src folder.

Also, when we build our code for production (bundling), Webpack will use this HTML as a template.

It is important to say that Django will not use this HTML file. This is the HTML entry point of webpack. Django will use the output of the webpack bundle.

Update your index.html file.

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="Django-React Integration Tutorial"/> <title>Django React Integration</title> </head> <body> <div id="root"></div> </body> </html>
Code language: HTML, XML (xml)

Step – 3 Create the React app

The index file is the root file of our app, meaning that all our codes will be connected to this root file.

The other tutorials or each boilerplate use this file for only render function of ReactDOM and leave it small and clear.

Writing this index file as it is is a totally a choice.

What we will do is as follows: We will create an Init component that will initialize the API framework (Apollo) and routing library (react-router-dom).

We will wrap our App.js file with the API framework so that all our components will be in the context of API.

The Apollo Provider expects an Apollo client.

The Apollo client has the information of the requested address, which is the address of our Django server.

After then we will wrap our App file again with the router component, namely Browser Router. This makes our app a single-page-application.

Thus, we make routing without rendering all the pages when the URL of the address bar changes.

At the end of the file, you will see the render function of ReactDOM which accepts our root component, which is the Init component in our case, and the DOM element that our app will be rendered in there.

Update your index.js file as follows.

import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import { BrowserRouter } from "react-router-dom" import ApolloClient from 'apollo-boost'; import { ApolloProvider } from '@apollo/react-hooks'; /* our frontend api client will make request to the adress we define in the first part. */ const apiclient = new ApolloClient({ uri: '<http:"" graphql="">', }); const Init = () => ( <apolloprovider client="{apiclient}"> <browserrouter> <app> </app></browserrouter> </apolloprovider> ) ReactDOM.render( <init>, document.getElementById('root')) </init></http:>
Code language: JavaScript (javascript)

Now, we are ready to create our simple movie app.

Our app has two different screens:

  • The main page which lists all movies in the database with less information
  • The movie page will show a specific movie page with more information.

Now update your App.js file.

import React from "react"; import { Route, Switch, Link } from "react-router-dom" // UI Screens we will define later import MoviePage from "./MoviePage.js" //New import MoviePage from "./MoviePage.js" //New import "./App.css" const App = () => { return ( <div className="App"> <Switch> <Route exact path="/" component={MainPage} /> {// colon before slug means it is a dynamic value // that makes slug parameter anything // like: /movie/the-lighthouse-2019 or /movie/anything // as long as slug matches with database. } <Route exact path="/movie/:slug" component={MoviePage} /> </Switch> </div>) } export default App
Code language: JavaScript (javascript)

Let me explain what that code means

When a user first opens our page, the switch component from react-router-dom will look at the URL. Then try to match the path of route components with this URL, if any, then the matched component in the route will be rendered.

Step – 4 Create page components and styling

The MovieList component will be shown on the landing page. Copy/Paste to “MovieList.js” file.

import React from "react"; import gql from "graphql-tag"; // our first query will requests all movies // with only given fields // note the usage of gql with 'jsvascript string literal' export const MOVIE_LIST_QUERY = gql` query movieList{ movieList{ name, posterUrl, slug } } ` const MovieList = (props) => { const { loading, error, data } = useQuery(MOVIE_LIST_QUERY); // when query starts, loading will be true until the response come. // At this time this will be rendered on screen if (loading) return <div>Loading</div> // if response fail, this will be rendered if (error) return <div>Unexpected Error: {error.message}</div>//if query succeed, data will be available and render the data return( <div classname="main-page"> {data && data.movieList && => ( <div classname="movie-card" key="{movie.slug}"> <img classname="movie-card-image" src="{movie.posterUrl}alt={" +="" "="" poster"}title="{" poster"}=""> <p classname="movie-card-name">{}</p> <link to="{`/movie/${movie.slug}`}" classname="movie-card-link"> </div>)) } </div>) } export default MovieList
Code language: PHP (php)

MoviePage component will show more details than the list view but only information about a specific movie.

Copy and paste the code MoviePage.js file.

import React from "react"; import gql from "graphql-tag"; // Note the usage of argument. // the exclamation mark makes the slug argument as required // without it , argument will be optional export const MOVIE_QUERY = gql` query movie($slug:String!){ movie(slug:$slug){ id, name, year, summary, posterUrl, slug } } `const MoviePage = (props) => { // uncomment to see which props are passed from router //console.log(props) // due to we make slug parameter dynamic in route component, // urlParameters will look like this { slug: 'slug-of-the-selected-movie' } const urlParameters = props.match.params const { loading, error, data } = useQuery(MOVIE_QUERY, { variables:{slug:urlParameters.slug} }); if (loading) return <div>Loading</div>if (error) return <div>Unexpected Error: {error.message}</div> return ( <div classname="movie-page"> <link to="/" classname="back-button">Main Page {data && && <div classname="movie-page-box"> <img classname="movie-page-image" src="{}alt={" +="" "="" poster"}title="{" poster"}=""> <div classname="movie-page-info"> <h1>{}</h1> <p>Year: {}</p> <br> <p>{}</p> </div> </div>} </div>) } export default MoviePage
Code language: PHP (php)

Add some styling: update App.css.

html, body { width:100vw; overflow-x: hidden; height:auto; min-height: 100vh; margin:0; } .App { position: absolute; left:0; right:0; display: flex; min-width: 100%; min-height: 100vh; flex-direction: column; background-color: #181818; /*font-family: "Open Sans", sans-serif;*/ font-size: 16px; font-family: sans-serif; } /* MAIN PAGE */ .main-page { position: relative; display: flex; flex-wrap: wrap; min-height: 40vh; background-color: #3f3e3e; margin:10vh 5vw; border-radius: 6px; } /* MOVIE CARD */ .movie-card { position: relative; width:168px; height:auto; background: #f1f1f1; border-radius: 6px; margin:16px; box-shadow: 0 12px 12px -4px rgba(0,0,0, 0.4); } .movie-card:hover { box-shadow: 0 12px 18px 4px rgba(0,0,0, 0.8); } .movie-card-image { width:168px; height:264px; border-top-left-radius: 6px; border-top-right-radius: 6px; } .movie-card-name { text-align: center; margin: 0; padding: 8px; font-weight: bold; } .movie-card-link { position: absolute; top:0; left:0; right: 0; bottom: 0; } /* MOVIE PAGE */ .back-button { position: absolute; left:10px; top:10px; width:120px; padding: 8px 16px; text-align: center; background: #f1f1f1; color:black; font-weight: bold; cursor:pointer; } .movie-page { position: relative; display: flex; flex-direction: column; justify-content: center; align-items: center; flex-wrap: wrap; min-height: 80vh; margin:10vh 10vw; border-radius: 6px; } .movie-page-box { position: relative; display: flex; height:352px; background-color: #f1f1f1; } .movie-page-image { width:280px; height:352px; } .movie-page-info { position: relative; display: flex; flex-direction: column; height:352px; width: auto; max-width: 400px; padding: 16px 32px; }
Code language: CSS (css)

Finally, Start Django-React App

Development Environment

In the development environment, we will run two different servers.

One is the Django server for backend, and the other one is the Webpack server for frontend development.

In the production environment, We will only run one Django server as I promise.

Go to the root folder of the Django project. ‘backend/’.

Execute the below command and make the Django server ready for frontend requests.

python runserver
Code language: CSS (css)

Open another terminal and go to FRONTEND directory. ‘backend/FRONTEND’

npm run start

You will see those screens.

Django and React Successfully Integrated

We created a simple single-page-application. Now, the last part of this tutorial will be made this app works seamlessly with our Django project.

Now you can stop the webpack server with the corresponding terminal screen.

The Final Part – Production Build of Django and React

Now, We can build our app for the production environment. Go to the FRONTEND directory and execute the build command.

npm run build

When the build process is done, there will be two Javascript files in the backend/static folder:

  • main.js
  • vendors~main.chunk.js

Also, check the backend/templates folder and you will see other index.html files.

This is the HTML file that Django will use.

I made this graphic in order to show webpack bundling process and how our app will use output files.

Leave a comment