I am using react-hot-loader and webpack. I also use webpack-dev-server together with an express backend.
This is my relevant webpack config for development:
var frontendConfig = config({
entry: [
'./src/client/app.js',
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/dev-server'
],
output: {
path: targetDir,
publicPath: PROD ? '/build/assets/' : 'http://localhost:3000/build/assets/' ,
filename: 'app.js'
},
module: {
loaders: [
{test: /\.js$/,
exclude: /node_modules/,
loaders: PROD ? [babelLoader] : ['react-hot', babelLoader] }
]
},
plugins: [
new webpack.HotModuleReplacementPlugin({ quiet: true })
]
});
with this config I start webpack and webpack-dev-server
gulp.task('frontend-watch', function() {
new WebpackDevServer(webpack(frontendConfig), {
publicPath: frontendConfig.output.publicPath,
hot: true,
stats: { colors: true }
}).listen(3000, 'localhost', function (err, result) {
if(err) {
console.log(err);
}
else {
console.log('webpack dev server listening at localhost:3000');
}
});
});
so webpack-dev-server is running at localhost:3000 and receives app.js from webpack watcher (which now is not anymore written to file system).
my express server serves as a backend/api and has the following config:
var express = require('express');
// proxy for react-hot-loader in dev mode
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({
changeOrigin: true,
ws: true
});
var isProduction = process.env.NODE_ENV === 'production';
// It is important to catch any errors from the proxy or the
// server will crash. An example of this is connecting to the
// server when webpack is bundling
proxy.on('error', function(e) {
console.log('Could not connect to proxy, please try again...');
});
module.exports = function (app) {
// We only want to run the workflow when not in production
if (!isProduction) {
console.log('setting up proxy for webpack-dev-server..');
// Any requests to localhost:4200/build is proxied
// to webpack-dev-server
app.all('assets/app.js', function (req, res) {
proxy.web(req, res, {
target: 'http://localhost:3000'
});
console.log('request proxied to webpack-dev!');
});
}
var server = require('http').createServer(app);
app.use(express.static(homeDirectory + '/build'));
app.use(express.static(homeDirectory + '/files'));
server.listen(4200);
};
That's all good so far, the proxying work for app.js and I see successfull hot update messages in the browser console:
Now, while it looks fine it does not work as I expected:
when I change a component's render() method it updates as supposed, but when I change a helper method (that is used in render()) then I don't get any hot update. is that normal?
Another thing that bugs me, if I work like this, and do a 'hard' browser reload at some point, all changes I made are reverted to the point where I started my webpack-dev-server - all the hot updates in between have not been persisted somehow. is that normal as well? I would expect that I loose my state but not any changes I made to the code in the meantime. That has probably something to with my app.js not being written to the file system.
For your question #2, that's not normal, I have a template repo that has HMR working available here and it works just fine https://github.com/briandipalma/wp-r-template
For question #1, usually render methods display or format data, not grab it from somewhere. But if you need to format data, use a function outside of the component
Parent component would call the following once you retrieve the price
<ChildComponent price={this.state.price}
ChildComponent's render function would use props (or better yet a parameter of the function). Remember: the whole point of React is composition and data flow
return (
<div>{this.props.price}</div>
);
Related
I'm dealing with an Angular 1 application that uses URI fragments for routing purposes.
For example:
http://localhost:3000/#/home
http://localhost:3000/#/menu
http://localhost:3000/#/list
Rather than:
http://localhost:3000/home
http://localhost:3000/menu
http://localhost:3000/list
When I'm using Webpack Dev Server to launch a local server with hot reloading, every time I apply some changes, Webpack reloads the URL but keeping the "/#/route" part.
Unfortunately, due to some code restrictions, I need to always reload the page from "/" because some redirections are applied internally when the app launches the first time and the app will crash if you try to start directly from any URI fragment.
I tried to use the "proxy" and "historyApiFallback" configurations in webpack.config.js like:
devServer: {
proxy: {
'/#': {
target: 'http://localhost:3000',
pathRewrite: { '^/#': '' }
}
}
And:
devServer: {
historyApiFallback: {
verbose: true,
rewrites: [{ from: /^\/#/, to: '/' }]
}
}
But so far, I didn't have any luck. The URL keeps the hash when reloading.
Any suggestions or ideas to configure Webpack Dev Server to always start from "/" and remove the hash URI?
I didn't find an example for a react host with and nextjs remote app
My host is working with a react remote app, now that I'm trying to add a nextjs remote app, it's not working:
Ports:
Host 3000
React remote 3001
NextJS Remote 3002
In my host react app I have the following, in the webpack.config.js file
plugins: [
new ModuleFederationPlugin({
name: 'react_container',
filename: 'remoteEntry.js',
remotes: {
remote_react: 'remote_react#http://localhost:3001/remoteEntry.js', // <- This is working
remote_nextjs: 'remote_nextjs#http://localhost:3002/_next/static/chunks/remoteEntry.js', // <- Not working :-(
},
exposes: {
'./react': 'react',
'./react-dom': 'react-dom',
},
shared: {
},
}),
And in the remote nextjs app in the next.config.js file
const { withFederatedSidecar } = require("#module-federation/nextjs-mf");
module.exports = withFederatedSidecar({
name: "remote_nextjs",
filename: "static/chunks/remoteEntry.js",
exposes: {
"./ExposedComponent": "./components/ExposedComponent",
},
shared: {
},
})({
// your original next.config.js export
});
And finally, in the App.jsx of the host , I'm trying to consume the remote components like
import RemoteNav from 'remote_react/Nav'; // <- Working
import ExposedComponent from 'remote_nextjs/ExposedComponent'; // <- Not working
I have repo here
The error I'm getting its 404 from
http://localhost:3000/_next/static/chunks/components_ExposedComponent_js.js
Thanks
From the webpack config of the host, I can see that the next JS app is running on port 3002.
You are getting 404 error for next JS remote bundles when the host is trying to fetch the bundles from next JS server, it is hitting port 3000 instead of 3002. This is because the next.config.js does not have the updated publicPath.
Add the following in the place where it says //your original next.config.js export in the next.config.js file
webpack: (config) => {
config.output.publicPath = "http://localhost:3002/_next/";
return config;
}
This will set the publicPath of your next JS app to port 3002 and fetch the bundles correctly.
Referring to https://webpack.js.org/guides/public-path/ ,
on your remote app's webpack config, set publicPath would fix the path issue.
export default {
output: {
publicPath: 'auto',
},
plugins: [...],
...
}
I am attempting to implement a service worker for a boilerplate project I'm working on (https://github.com/jonnyasmar/gravity-bp feedback welcome!), but I've hit a snag :(
Problem:
I'm serving the index.html for this boilerplate virtually as an interpreted Twig template via ExpressJS. However, because I'm generating the service worker assets at build time and that is where I'm pointing it to the cacheable static assets, I can't figure out how to tell the service worker that I want it to cache the virtual index.html file served by ExpressJS at runtime.
My most successful attempts successfully cache all static assets (including the asset-manifest.json generated at build time), but will not cache a virtual index.html.
If I convert it to a static html file, the service worker does successfully cache it.
Please be sure to upvote this question to help get some visibility if you are interested in the answer!
Questions:
Is there a way to correct my code to accomplish this?
Is there anything wrong with doing it this way?
If yes to #2, how would you recommend handling this and why?
See the full source on GitHub.
Relevant code:
webpack.config.js:
output: {
filename: '[name].js',
chunkFilename: '[chunkhash].js',
path: path.resolve(__dirname, 'public'),
publicPath: '/'
},
plugins: {
new ManifestPlugin({
fileName: 'asset-manifest.json',
}),
new SWPrecacheWebpackPlugin({
cacheId: 'gravity-bp',
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'sw.js',
minify: true,
navigateFallback: 'index.html',
stripPrefix: 'public/',
swFilePath: 'public/sw.js',
staticFileGlobs: [
'public/index.html',
'public/**/!(*map*|*sw*)',
],
})
}
sw.ts:
const swUrl: string = 'sw.js';
export const register = (): void =>{
if(process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator){
const sw: ServiceWorkerContainer = navigator.serviceWorker;
sw.register(swUrl).then(registration =>{
registration.onupdatefound = (): any =>{
const installer: ServiceWorker = registration.installing;
installer.onstatechange = (): any =>{
if(installer.state === 'installed'){
if(sw.controller){
console.log('New content available.');
}else{
console.log('Content cached for offline use.');
}
}
};
};
}).catch((error) =>{
console.error('Failed to register service worker:', error);
});
}
};
export const unregister = (): void =>{
if('serviceWorker' in navigator){
navigator.serviceWorker.ready.then(registration =>{
registration.unregister();
});
}
};
server.ts:
import * as path from 'path';
const twig = require('twig').__express;
const express = require('express');
const compression = require('compression');
const pkg = require('../../package.json');
const version = pkg.version;
let app = express(),
ip = '0.0.0.0',
port = 3000,
views = path.resolve('./src/views');
app.use(compression());
app.use(express.static('public'));
app.set('view engine', 'twig');
app.engine('.twig', twig);
app.set('views', views);
// Routes
app.get("*", function(req: any, res: any, next: any){
// vars
res.locals.version = version;
res.render('index');
});
let server = app.listen(port, ip, function(){
let host = server.address().address;
let port = server.address().port;
console.log('Gravity Boilerplate ready at http://%s:%s', host, port);
});
Within the sw-precache-webpack-plugin documentation it talks about using sw-precache options. The one you should investigate is the dynamicUrlToDependencies setting. See some of these links for more info:
https://github.com/GoogleChromeLabs/sw-precache/issues/156
dynamicUrlToDependencies [Object⟨String,Buffer,Array⟨String⟩⟩]
For example, maybe start with this to test:
dynamicUrlToDependencies: {
'/': 'MAGIC_STRING_HERE'
},
So really, you need to configure the sw-precache WebPack plugin to load a server rendered page as the navigateFallback route.
Sample repo demonstrating the issue is here.
I'm trying to set up webpack dev server so that:
Requests to / are served by public/landing.html (a static landing page)
Requests to anything else are served by my React app
Using create-react-app, and based on webpack-dev-server's options, I've set up my webpackDevServer.config.js as follows:
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387.
disableDotRule: true,
rewrites: [
// shows views/landing.html as the landing page
{ from: /^\/$/, to: 'landing.html' },
// shows views/subpage.html for all routes starting with /subpage
{ from: /^\/subpage/, to: 'subpage.html' },
// shows views/404.html on all other pages
{ from: /./, to: '404.html' },
],
},
And when I start webpack here's what I see:
Requests to /subpage are routed correctly to subpage.html
Requests to /foo are routed correctly to 404.html. Eventually, these would be handled by my React app.
Requests to / are routed incorrectly to my React app.
How can I get landing.html to respond to requests at /?
I ended up opening a bug request with webpack-dev-middleware and discovered it was not a bug but a failure of configuration.
Specifically, the issue is using HtmlWebpackPlugin alongside historyApiFallback. I believe plugins are processed before the regex matching, and HtmlWebpackPlugin's default file output is index.html; this means that out of the box, / will always be routed to the HtmlWebpackPlugin output file.
The solution to this is to set a custom filename in HtmlWebpackPlugin, which allows you to control the matching again. Here's a sample repo demonstrating the fix and here's the webpack config:
module.exports = {
context: __dirname,
entry: [
'./app.js',
],
output: {
path: __dirname + "/dist",
filename: "bundle.js"
},
devServer: {
publicPath: "/",
// contentBase: "./public",
// hot: true,
historyApiFallback: {
// disableDotRule: true,
rewrites: [
{ from: /^\/$/, to: '/public/index.html' },
{ from: /^\/foo/, to: '/public/foo.html' },
{ from: /(.*)/, to: '/test.html' },
],
}
},
plugins: [
new HtmlWebpackPlugin({
title: "Html Webpack html",
hash: true,
filename: 'test.html',
template: 'public/plugin.html',
}),
],
};
Two thoughts come to mind:
What you want to do is not possible with Webpack Dev Server (as far as I'm aware)
And, once npm run build is run and deployed the deployed app would not follow the same rules as configured in Webpack Dev Server
Even if I'm mistaken on #1, #2 seems like a bigger issue if you ever plan to deploy the app. This leads me to recommend an alternate setup which will work on dev and production (or wherever it's deployed).
A few options:
setup the app as a single-page app (SPA) and use React Router to serve the static routes (/ and /subpage) and wildcards (everything else)
setup node (or another server) to serve the static routes (/ and /subpage) and wildcards (everything else)
An Attempt
In an attempt to setup the routes I was able to achieve this setup:
Display landing.html when / is requested
Display subpage.html when /subpage is requested
Display the React App at a specific path, like app.html
To do this make the following changes:
Move /public/index.html to /public/app.html
mv ./public/index.html ./public/app.html
In /config/webpackDevServer.config.js, update historyApiFallback to:
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387.
disableDotRule: true,
rewrites: [
// shows views/landing.html as the landing page
{ from: /^\/$/, to: "landing.html" },
// shows views/subpage.html for all routes starting with /subpage
{ from: /^\/subpage/, to: "subpage.html" },
// shows views/app.html on all other pages
// but doesn't work for routes other than app.html
{ from: /./, to: "app.html" }
]
}
In /config/paths.js update appHTML to:
appHtml: resolveApp("public/app.html")
In /config/webpack.config.dev.js, updateplugins` to include:
new HtmlWebpackPlugin({
filename: "app.html",
inject: true,
template: paths.appHtml
})
Requesting a random URL, like localhost:3000/foo will return a blank page but contains the HTML from the app.html page without the bundled <script> tags injected. So maybe you can find a solution to this final hurdle.
Update current problem :
it seems that the webpack hot loader goes wrong,because when i run the following cmd:webpack,it can be built as usual.but when i run ""dev": "webpack-dev-server --color --hot --progress && node ./server.js"".webpack cannot generate built files for me .
my webpack.config is as follows:
module.exports = {
entry: getEntries(),
.....
function getEntries(){
var routeDir = path.join(SRC_DIR,"javascripts","routes");
var routeNames = routeDir?fs.readdirSync(routeDir):[];
var nameMaps = {};
routeNames.forEach(function(routeName){
var filename = routeName.match(/(.+)\.js$/)[1];
console.log("filename in entry ",filename);
if(filename){
var devEntryPath = [
'webpack-dev-server/client?http://127.0.0.1:3001', // WebpackDevServer host and port
'webpack/hot/only-dev-server',
path.join(routeDir,filename)
];
nameMaps[filename] = devEntryPath;
}
});
return nameMaps;
}
server.js
var server = new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: true
}).listen(3001,'localhost',function(err,result){
if(err) console.log(err);
console.log("webpack listening at port 3001");
});
var app = express();
app.get("/monitor/index",function(req,res){
res.sendFile(__dirname+"/src/views/"+"page1.html");
});
app.get("/monitor/category/*",function(req,res){
res.sendFile(__dirname+"/src/views/"+"page2.html");
});
app.use(express.static(__dirname))
.listen(9090, 'localhost', function (err, result) {
if (err) console.log(err);
console.log('Listening at localhost:9090');
});
finally,i found where the problem is,and know the relationship between webpack-dev-server and my express server.
when using hot-loader with webpack-dev-server:
step1:the webpack build the input file to the publicPath (which was designated in "output" of webpack.config.js).
step2,the node server will send html to the front,and search for the related assets(such as js,img etc),but where? we can change the script(related with html) path to the webpack-dev-server.(just generated by step1),so node-server will ask webpack-dev-server for help.
to sum up ,i modified 3 places:
publicPath of webpackDevServer
webpack output(publicPath),equal to above
script path in html.
that's all.and now,my project can run as expected.