I'm using Django 4 and wish to integrate a ReactJS application within the Django framework.
I chose to use the exact approach here to start and installed it identically as outlined.
Here's a list of the installed components and versions:
├── #babel/core#7.20.12
├── #babel/preset-env#7.20.2
├── #babel/preset-react#7.18.6
├── babel-loader#9.1.2
├── clean-webpack-plugin#4.0.0
├── css-loader#6.7.3
├── react-dom#18.2.0
├── react#18.2.0
├── style-loader#3.3.1
├── webpack-bundle-tracker#0.4.3
├── webpack-cli#5.0.1
└── webpack#5.75.0
I do not want to post all of my code here since it is 100% identical to the code in the link above.
Unfortunately, I'm receiving a strange error in the console:
GET http://127.0.0.1:8001/frontend/static/frontend/frontend-dc3188e75be82e0a01e0.js net::ERR_ABORTED 404 (Not Found)
127.0.0.1/:1 Refused to execute script from 'http://127.0.0.1:8001/frontend/static/frontend/frontend-dc3188e75be82e0a01e0.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
This appears to be related to the path, but the referenced JS filename is spelled correctly and exists at the exact referenced path.
The server console also displays a similar error:
Not Found: /frontend/static/frontend/frontend-dc3188e75be82e0a01e0.js
the link you provided is an old tutorial. You should always follow the documentation. Based on docs
You must generate the front-end bundle using webpack-bundle-tracker
before using django-webpack-loader. You can compile the assets and
generate the bundles by running:
npx webpack --config webpack.config.js --watch
this is webpack config setting related to webpack-bundle-tracker. It spits out the webpack-stats.json file and looks like django webpack-loader reads from this .json file
const path = require('path');
const webpack = require('webpack');
const BundleTracker = require('webpack-bundle-tracker');
module.exports = {
context: __dirname,
entry: './assets/js/index',
output: {
path: path.resolve('./assets/webpack_bundles/'),
filename: "[name]-[hash].js"
},
plugins: [
new BundleTracker({filename: './webpack-stats.json'})
],
}
and then:
WEBPACK_LOADER = {
'DEFAULT': {
'CACHE': not DEBUG,
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
'POLL_INTERVAL': 0.1,
'IGNORE': [r'.+\.hot-update.js', r'.+\.map'],
}
Thank you for the feedback. This is how I ultimately performed this integration entirely using a combination of online tutorials, the feedback here and the github repo for django-webpack-loader.
Please see the comment at the very bottom for details on resolving the path issue with DigitalOcean Spaces.
Keep in mind this approach below eliminates a lot of the unnecessary steps and ensures the latest versions of the software are used. References are below.
If React and React-Dom are not already installed into your environment, start here:
npm install react react-dom
A: Setup the Django App and Dependencies
Create the frontend app in Django
python manage.py startapp frontend
Update the list of installed apps within settings.py or base.py:
INSTALLED_APPS = [
'frontend',
...
]
Create the templates folder for the frontend app. In my case, my Templates directory is in the root of the project, so under the folder I added 'frontend': ./templates/frontend
Create the template in the newly created frontend folder: ./templates/frontend/index.html
Add the following into the index.html, which contains Django tags to use later for rendering the bundle:
<!DOCTYPE html>
{% load render_bundle from webpack_loader %}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Site</title>
</head>
<body>
<div id="app"></div>
<p>hello</p>
{% render_bundle 'frontend' %}
</body>
</html>
Create the view in the app frontend/views.py
from django.shortcuts import render
def index(request):
return render(request, 'frontend/index.html')
Update the URL pattern within urls.py at the project level: config/urls.py. In this case, the react app is loading from the root of the domain. I recommend using this configuration (outlined in step 7 and 8) or you will receive path errors. This can be changed later.
path('', include('frontend.urls'))
Update the URL patterns within urls.py at the app level:
path('', views.index)
At this point, you can test and run the server or continue on. If you test, remember that you have 2 Django tags loading in Step 5 that you will need to comment out.
python manage.py runserver
B: Install React & Babel
From the root of the project, run the following command to create the package.json file.
npm init -y
Setup Babel.
npm install --save-dev #babel/core
Install Babel plugins:
npm install --save-dev #babel/preset-env #babel/preset-react
Create a .babelrc file in the root of your project and add this into it:
{
"presets": ["#babel/preset-env", "#babel/preset-react"]
}
C: Install & Configure Webpack
Install Webpack and the additional packages.
npm install --save-dev webpack webpack-cli webpack-bundle-tracker babel-loader css-loader style-loader clean-webpack-plugin
Create a webpack.config.js file in the root of the project.
const path = require('path');
const webpack = require('webpack');
const BundleTracker = require('webpack-bundle-tracker');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
context: __dirname,
entry: {
frontend: './frontend/src/index.js',
},
output: {
path: path.resolve('./frontend/static/frontend/'),
filename: '[name]-[hash].js',
publicPath: 'static/frontend/',
},
plugins: [
new CleanWebpackPlugin(),
new BundleTracker({ filename: './webpack-stats.json' })
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
}
Add the following scripts to your package.json file located in the project root. You should already have scripts in this file; just add the two new lines of code for "dev" and "build".:
{
...,
"scripts": {
...,
"dev": "webpack --config webpack.config.js --watch --mode development",
"build": "webpack --config webpack.config.js --mode production"
}
}
C: Create a basic React APP for testing
In the frontend app folder, create a folder called src.
In the src folder, create an App.js file. Be sure to follow the capitalization. And, add the following to it:
import React from 'react'
const App = () => {
return (
<div>Hello, World!</div>
)
}
export default App
In the src folder, create an index.js file and add the following to it:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(
<App />,
document.getElementById('app')
)
D: Install Webpack Loader & Configure
Run the following command:
pip install django-webpack-loader
Update the list of installed apps within settings.py or base.py:
INSTALLED_APPS = (
...
'webpack_loader',
...
)
Also, update the Webpack Loader configurations in the settings.py or base.py file:
WEBPACK_LOADER = {
'DEFAULT': {
'CACHE': not DEBUG,
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
'POLL_INTERVAL': 0.1,
'IGNORE': [r'.+\.hot-update.js', r'.+\.map'],
}
}
If you receive an error related to DEBUG, check the available options in the django-webpack-loader instructions (see below for link).
E: Compile the frontend asset files and test
If you commented out the django tags in step A9, remove the comment before proceeding.
Run the following command:
npx webpack --config webpack.config.js --watch
Run your server:
python manage.py runserver
Open your browser and navigate to the root of your site. You should receive a Hello World printout on the screen. If not, and you only receive an empty white screen, the react assets are not loading. Check the server console and/or the browser console for errors.
References:
Outdated Tutorial
https://dev.to/zachtylr21/how-to-serve-a-react-single-page-app-with-django-1a1l
django-webpack-loader repo
https://github.com/django-webpack/django-webpack-loader
Related
I have created an app in react.js with Vitejs, I have included a 3D model with Theejs (.glb file). When I use npm run dev my 3D model works perfectly without errors, but when I run npm run build the 3D model is not included in the dist/assets folder, only js, css and images files are included. How can I fix it? I feel that there is something wrong with the vite configuration as the paths are well placed.
This is my vite.config.js file
export default defineConfig({
plugins: [react()],
root: './',
build: {
chunkSizeWarningLimit: 3000,
outDir: 'dist',
},
publicDir: 'assets' ,
})```
and this is how I am loading my model in the component
const { nodes } = useGLTF("public/portal.glb")
You can use explicit url import.
import modelSrc form 'model/yourmodel.glb?url';
then
const { nodes } = useGLTF(modelSrc);
The model file will be assigned in [outDir]/assets after build.
In order to use this package, I think I need to register it. I've already installed it with npm.
I created the project with npx create-react-project my-project. I tried putting in the requirements into index.js like so...
import reportWebVitals from './reportWebVitals';
import postcss from 'postcss';
postcss([ require('postcss-modules-values-replace', require('postcss-calc'))])
But I got an error Cannot statically analyse 'require(...,...) and it looks like it's because I'm supposed to do something with webpack but I don't see any webpack.js files in my project so I'm not sure how to register the plugin.
Place it before other plugins:
postcss([ require('postcss-modules-values-replace'), require('postcss-calc') ]);
create postcss.config.js file and define the plugins
module.exports = (ctx) => ({
plugins: [
require('postcss-modules-values-replace')({fs: ctx.webpack._compiler.inputFileSystem}),
require('postcss-calc'),
]
});
I have a custom npm package created in directory web_with_weback (web react project). The package is built using webpack and contains one .png image. The package is loaded by using npm link inside another directory which is a react native project named mobile_with_metro. So in short, I want to have an npm package which among other things will share images from web to mobile. So to give you some context, here is how the two projects are setup.
The web project has the following structure.
Where the index.ts contains:
import TestImage from './testimage.png';
export {
TestImage
};
webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
entry: {
images: {import: './src/images/index.ts', filename: 'images/index.js'}
},
externals: [nodeExternals()],
output: {
libraryTarget: 'commonjs'
},
plugins: [
new CleanWebpackPlugin(),
new CopyPlugin({
patterns: [
{ from: "src/images/index.d.ts", to: "images/index.d.ts" }
],
}),
],
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset/inline'
}
]
}
}
I use webpack (v5.19.0). I use the latest feature from webapack asset/inline which is a replacement for the previous used url-loader.
dist folder after webpack build looks the following.
By using npm link in the dist folder and npm link #test/web_with_webpack in mobile_with_metro I make the built package available in the mobile app directory.
Now when I try to import the image and render it in an Image component in react native I get the following error:
error: Error: Unable to resolve module `#test/web_with_webpack/images` from `App.tsx`: #test/web_with_webpack/images could not be found within the project or in these directories:
node_modules/#test/web_with_webpack
If you are sure the module exists, try these steps:
1. Clear watchman watches: watchman watch-del-all
2. Delete node_modules: rm -rf node_modules and run yarn install
3. Reset Metro's cache: yarn start --reset-cache
4. Remove the cache: rm -rf /tmp/metro-*
at ModuleResolver.resolveDependency (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:186:15)
at ResolutionRequest.resolveDependency (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/node-haste/DependencyGraph/ResolutionRequest.js:52:18)
at DependencyGraph.resolveDependency (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/node-haste/DependencyGraph.js:287:16)
at Object.resolve (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/lib/transformHelpers.js:267:42)
at /home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/DeltaBundler/traverseDependencies.js:434:31
at Array.map (<anonymous>)
at resolveDependencies (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/DeltaBundler/traverseDependencies.js:431:18)
at /home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/DeltaBundler/traverseDependencies.js:275:33
at Generator.next (<anonymous>)
at asyncGeneratorStep (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/DeltaBundler/traverseDependencies.js:87:24)
Here is the react native App.tsx which imports the image.
import React from 'react';
import {
Image
} from 'react-native';
import {TestImage} from '#test/web_with_webpack/images';
const App = () => {
return (
<>
<Image source={{
uri: TestImage
}}/>
</>
);
};
To me it seems that the metro bundler can't understand what webpack has generated as an image 'path'.
Is there anything that I am missing here?
EDIT:
I tried using it in another react web project (which is setup with CRA) and it works. So, there is something that metro bundler in react native does not understand in the built package.
I am trying to set up Storybook in a project. My project is runing on react#^16, and I'm using typescript, with a custom babel and webpack setup for development and build. To set up storybook, I did
npx sb init
This installs everything needed. It puts a .storybook folder in the root folder, and a stories folder in my src folder with some prefab components and stories in tsx format (which is what I want):
The .storybook/main.js file seems fine:
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials"
]
}
And the average .stories.js file automatically installed by npx sb init also seems fine:
import React from 'react';
// also exported from '#storybook/react' if you can deal with breaking changes in 6.1
import { Story, Meta } from '#storybook/react/types-6-0';
import { Header, HeaderProps } from './Header';
export default {
title: 'Example/Header',
component: Header,
} as Meta;
const Template: Story<HeaderProps> = (args) => <Header {...args} />;
export const LoggedIn = Template.bind({});
LoggedIn.args = {
user: {},
};
export const LoggedOut = Template.bind({});
LoggedOut.args = {};
But when I run npm run storybook, the storybook landing page has no stories. Even though it had installed some default stories to start playing with. It says:
Oh no! Your Storybook is empty. Possible reasons why:
The glob specified in main.js isn't correct.
No stories are defined in your story files.
As requested, here is a link to the repo so you can dig a bit deeper into the structure, weback config, etc. Note I have not committed the npx sb init changes yet, so you won't see the files there, only my starting point just before running the sb init.
I haven't had any issues getting npx sb init to work with a standard create-react-app, but with my custom webpack build and typescript, its just empty. What's going wrong?
Edit: Additional detail
I realize that just running npx sb init, then npm run storybook throws this error:
ERROR in ./.storybook/preview.js-generated-config-entry.js
Module not found: Error: Can't resolve 'core-js/modules/es.array.filter'
Based on this thread, installing core-js#3 solves the problem and storybook runs, though with no stories.
It seems like the babel plugin transform-es2015-modules-amd doesn't fit right with storybook since sb still uses your babel configuration.
You might need to remove it then it would work:
{
"plugins": [
// "transform-es2015-modules-amd", // Remove this plugin
]
}
If you want to have a special babel configuration for storybook, place it .storybook/.babelrc so the configuration would be simple like this:
.storybook/.babelrc:
{
"presets": ["#babel/preset-env", "#babel/preset-react", "#babel/preset-typescript"]
}
NOTE: You might miss to forget install #babel/preset-typescript to help you transform your typescript code.
Maybe you have problems with the stories path, try to save only "../src/**/*.stories.js" in your config to see if its the reason
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
]
In case of dealing with arcgis-js-api in sb, you have to declare #arcgis/webpack-plugin in storybook's webpack configuration by adding to its config.
Here are a few steps you have to do:
Add webpackFinal property in .storybook/main.js with following content:
const ArcGISPlugin = require('#arcgis/webpack-plugin');
module.exports = {
// ...
webpackFinal: (config) => {
// Add your plugin
config.plugins.push(
new ArcGISPlugin(),
);
// Since this package has used some node's API so you might have to stop using it as client side
config.node = {
...config.node,
process: false,
fs: "empty"
};
return config;
}
};
One more thing to be aware of, some components are importing scss files, so you might need to support it by adding a scss addon '#storybook/preset-scss'
// Install
npm i -D #storybook/preset-scss css-loader sass-loader style-loader
// Add to your current addons
{
addons: ['#storybook/addon-links', '#storybook/addon-essentials', '#storybook/preset-scss'],
}
Like a tmhao2005 say. Storybook still uses your babel configuration. And this is the intended behavior. This thread at github also describes how the fix similar issue.
Updated your config .storybook/main.js.
If you use .babelrc:
babel: async options => ({ ...options, babelrc: false })
Or .babel.config.js:
babel: async options => ({ ...options, configFile: false })
Github repo with everything in: https://github.com/b-paul/react-lifecycle
Update 12/18: A large part of the problem was the npm commands used to run the project. I had noticed that npm build was not successful, but npm start reported building OK. Full answer below on why that didn't work as expected. The rest of this question is being kept for posterity.
I'm having trouble with basic setup for my first webpack project. I'm using React and Babel, and the following webpack.config.js:
var path = require('path');
module.exports = {
entry: [ path.resolve('./js/app.js'),
'webpack-dev-server/client?http://localhost:8080' ],
output: {
path: path.resolve('./js/build'),
filename: 'app.min.js'
},
module: {
loaders: [
{ test: /\.jsx?$/,
loader: 'babel',
query: {
presets: ['react', 'stage-1', 'es2015']
},
include: path.resolve('js/') } ]
},
devtool: 'source-map'
};
js/app.js
import Lifecycle from './components/lifecycle';
import React from 'react';
import {render} from 'react-dom';
var shell = document.createElement('div');
shell.className = 'app-shell';
document.body.appendChild(shell);
render(<Lifecycle />, shell);
js/components/lifecycle.js
import React from 'react';
class Lifecycle extends React.Component {
render() {
return (
<div>Hello, world</div>
);
}
}
export default Lifecycle;
The above builds without errors, but it won't render "Hello, world". I'm getting the error, "Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined." in the browser console.
I can trace this as far as some generated babel code that tries to check if my module Lifecycle is an ES6 module (babel recognizes it as such) and expects it to have a property default on the internal module object (it doesn't). Here is that generated code:
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _lifecycle = __webpack_require__(1);
var _lifecycle2 = _interopRequireDefault(_lifecycle);
var _react = __webpack_require__(2);
var _react2 = _interopRequireDefault(_react);
var _reactDom = __webpack_require__(159);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
(0, _reactDom.render)(_react2.default.createElement(_lifecycle2.default, null), document.body);
/***/ }
Final note: I referenced https://github.com/code0wl/react-example-es2015 several times for setup, and I can clone and build that example repo into a working app with no problems. I'm realize that I must've missed some essential part of what's happening in that repo, but I can't see it.
works for me
git clone https://github.com/b-paul/react-lifecycle.git
cd react-lifecycle.git
npm install
npm run build
npm run start
# go to http://localhost:8090/index.html
Is there are reason for creating your app's container div dynamically in app.js rather than just putting it in index.html? I cloned your repo and got the app to build by making the following changes:
index.html:
<body>
<div id="app-shell"></div>
<script type="application/javascript" src="js/build/app.min.js"></script>
</body>
app.js
import React from 'react';
import {render} from 'react-dom';
import Lifecycle from './components/lifecycle';
render(<Lifecycle />, document.getElementById('app-shell'));
I think the “Element type is invalid” error was a red herring. Prior to that I was seeing:
Warning: React.createElement: type should not be null, undefined,
boolean, or number. It should be a string (for DOM elements) or a
ReactClass (for composite components).warning #
app.min.js:2233createElement # app.min.js:19531(anonymous function) #
app.min.js:61__webpack_require__ # app.min.js:20(anonymous function) #
app.min.js:40(anonymous function) # app.min.js:43 app.min.js:2233
Warning: render(): Rendering components directly into document.body is
discouraged, since its children are often manipulated by third-party
scripts and browser extensions. This may lead to subtle reconciliation
issues. Try rendering into a container element created for your app.
Which is likely due to the way you are creating the shell element in app.js...
This question should have been called "why won't my NPM scripts update my bundle."
At some point early in development, I had successfully bundled my code with webpack. That bundle was on disk as js/build/app.min.js. After that point, I thought I was using NPM scripts from package.json to rebuild the app with each change, but in-browser, I was still getting the old behavior (from the original bundle). I missed two facts:
Fact 1: npm build is a recognized NPM command, but it does not invoke scripts.build from package.json. As in #zhongjie-wu 's answer, I needed to do npm run build. npm start, on the other hand, does invoke scripts.start, which successfully built a bundle with webpack-dev-server.
Fact 2: webpack-dev-server does not recognize output.path from the webpack config file as part of the route when serving the rebuilt bundle. Because of this, given the configuration I used, webpack builds to /js/build/app.min.js, but webpack-dev-server serves that bundle from memory as /app.min.js. Since my HTML references the original, I didn't see the dev server bundle.
Layout.js
export default class Layout extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="Layout">
</div>
)
}
};
.babelrc
need add stage-1 preset for try transpiling classes
{
"presets": ["stage-1", "es2015", "react"],
"plugins": ["transform-runtime"]
}
and entry file need doing ES5 export:
import React from 'react';
import Layout from './Layout';
import {render} from 'react-dom';
if (typeof document !== 'undefined') {
render(<Layout />, document.getElementById('app'));
}
module.exports = Layout;
its working for me