Webpack Splitting React Components into "0.js" "1.js" etc. etc - reactjs

I am unable to re-create the splitting of React components into separate files e.g. 0.js, 1.js, 2.js etc. for code-splitting and reducing the bundle.js file. Does anyone happen to know how this outcome would be produced? I tried recreating it with ChunkManifest and webpack-manifest plugins but it just wouldn't do it. Any advice would be awesome!
routes.js
function errorLoading(err) {
console.error('Dynamic page loading failed', err);
}
function loadRoute(cb) {
return (module) => cb(null, module.default);
}
export default [
{
path: '/',
component: App,
childRoutes: [
{
path: 'signup',
getComponent(location, cb) {
System.import('./modules/App/components/Authentication/Login.js')
.then(loadRoute(cb))
.catch(errorLoading);
}
},
{
path: 'verify',
getComponent(location, cb) {
System.import('./modules/App/components/Authentication/Verify.js')
.then(loadRoute(cb))
.catch(errorLoading);
}
},
{
path: 'password-reset',
getComponent(location, cb) {
System.import('./modules/App/components/Authentication/PasswordReset.js')
.then(loadRoute(cb))
.catch(errorLoading);
}
},
{
path: 'new-password',
getComponent(location, cb) {
System.import('./modules/App/components/Authentication/NewPassword.js')
.then(loadRoute(cb))
.catch(errorLoading);
}
}
]
}
]

The kind of code splitting is accomplished in a few ways:
require.ensure()
System.import (This will be deprecated in webpack v3)
import()
Here is a link from our new docs page which specifies some examples of code splitting with react. https://webpack.js.org/guides/lazy-load-react/
(You can see here it is also referred to as lazy-loading modules)

Related

Storybook re-renders story after receiving MSW response

When I trigger requests to the backend within a Storybook story, the story is re-rendered after receiving the response from Mock Service Worker (MSW). How can I prevent the re-rendering from happening?
Here's the story:
I followed the tutorial on https://storybook.js.org/addons/msw-storybook-addon to set up MSW for my Storybook stories.
So I:
installed msw and msw-storybook-addon
generated a service worker: npx msw init static/
updated my preview.js to call the initialize() function, added the mswDecorator, and set some global handlers (e. g. for "/api/customers")
When opening a component in Storybook that includes a request to "/api/customers" the following happens:
Initially MSW is telling me that it's enabled: [MSW] Mocking enabled.
I click a button to manually send a request to "/api/customers"
MSW is telling me that this API request is covered: [MSW] 15:21:20 GET /api/customers/ (200 OK)
I print the request results on the console - that works and I can see the expected results printed
But right after that the story is unintentionally re-rendered - this is what I want to suppress.
FF is telling me:
The connection to http://localhost:6006/__webpack_hmr was interrupted
while the page was loading.
Chrome doesn't give me that piece of information.
Here's the setup:
package.json:
{
...
"msw": {
"workerDirectory": "static"
}
}
main.js (Storybook):
// imports ...
const toPath = (filePath) => path.join(process.cwd(), filePath);
module.exports = {
core: {
builder: "webpack5",
},
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.#(js|jsx|ts|tsx)"],
addons: [
"#storybook/addon-links",
"#storybook/addon-essentials",
],
features: {
emotionAlias: false,
},
webpackFinal: (config) => {
return {
...config,
module: {
...config.module,
rules: custom.module.rules, // rules how to handle file extensions
},
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
"#emotion/core": toPath("node_modules/#emotion/react"),
"emotion-theming": toPath("node_modules/#emotion/react"),
},
},
};
},
staticDirs: ["../path/to/static/"], // that's our public
};
Here's some code:
preview.js
export const parameters = {
...
msw: {
handlers: [
rest.get("/api/customers", (req, res, ctx) => {
let customers = ...;
return res(
ctx.json({
data: customers,
})
);
}),
],
},
}
ExampleStory.stories.jsx:
export default {
title: "Customers",
};
const getCustomers = () => {
// ... set axios config etc.
axios.get("/api/customers", config)
.then((response) => response.data.data)
.then((customers) => console.log(customers)); // yep, the customers gets printed!
};
export const ExampleStory = () => {
return (
<div>
Simple demo story
<button onClick={getCustomers}>Get customers</button>
</div>
);
};
Node Modules:
Storybook#6.5.9
msw#0.42.3
msw-storybook-addon#1.6.3

NextJS - Routing with different language

I try to create multilanguage website with Next JS.
I'm using next-translate package for translation.
For the posts I use static generation.
|-> pages
|-> post
|-> [slug].js
|-> i18n.json
|-> next.config.js
Problem
When I use default language ('tr') I successfully navigate http://localhost:3000/post/title-tr
But If I changed to language to 'en', library add 'en' to path then try to navigate http://localhost:3000/en/post/title-en and returns 404 page.
Solution I tried
In next.config.js I add rewrites method. But It didn't work.
require('dotenv').config();
const nextTranslate = require('next-translate');
module.exports = nextTranslate({
async rewrites() {
return [
{
source: '/en/post/:path*',
destination: '/post',
},
]
},
env: {
NEXT_PUBLIC_URL: process.env.NEXT_PUBLIC_URL,
},
})
Where should I solve this problem? Routing, config files or Link component?
i18n.json
{
"locales": ["tr", "en"],
"defaultLocale": "tr",
"pages": {
"*": ["common","country","position"]
}
}
Link component I used
<Link key={`L${item.id}`} href="/post/[slug]" as={`/post/${slug(item.title)}-${item.id}`}></Link>
[slug].js
function Post({ data }){
return <>...</>
}
export async function getStaticPaths() {
const { data } = await fetch('fromAPI');
return {
paths: data.map(item => {
return {
params:
{
slug: `${slug(item.title)}-${item.id}`
}
}
}),
fallback: false
}
}
export async function getStaticProps({ params }) {
const { data } = await fetch('fromAPI');
return { props: { data} }
}
export default Post;
You have to add a path for every locale as shown in the documentation: https://nextjs.org/docs/advanced-features/i18n-routing#dynamic-routes-and-getstaticprops-pages
// [slug].js
export const getStaticPaths = ({ locales }) => {
return {
paths: [
{ params: { slug: 'post-1' }, locale: 'tr' },
{ params: { slug: 'post-1' }, locale: 'en' },
],
fallback: true,
}
}
When I use default language ('tr') I successfully navigate http://localhost:3000/post/title-tr
But If I changed to language to 'en', library add 'en' to path then
try to navigate http://localhost:3000/en/post/title-en and returns 404
page.
If no locale is provided only the defaultLocale will be generated.
If fallback is false, then any paths not returned by getStaticPaths will result in a 404 page. So right now you're only generating the defaultLocale "tr" and all other paths with different locales will result in a 404 page.

When I refresh the react.js and next.js page, it gives me 404 error

I have a React.js and Next.js web site, it works well until I export production build. When I publish my project on the server, I get this problems: When I refresh the page it gives me 404 error.
I found Next.js 404 error after refreshing the page and set its configs. but my problem still exists.
All my links are like this:
import Link from 'next/link';
export default () => (
<Link href="/profile">
<a>profile</a>
</Link>
)
For static HTML export you must use exportPathMap
Refer: https://nextjs.org/docs/api-reference/next.config.js/exportPathMap
module.exports = {
exportPathMap: async function (
defaultPathMap,
{ dev, dir, outDir, distDir, buildId }
) {
return {
'/': { page: '/' },
'/profile': { page: '/profile' },
'/p/hello-nextjs': { page: '/post', query: { title: 'hello-nextjs' } },
'/p/learn-nextjs': { page: '/post', query: { title: 'learn-nextjs' } },
'/p/deploy-nextjs': { page: '/post', query: { title: 'deploy-nextjs' } },
}
},
}
Add this to next.config.js
module.exports = {
trailingSlash: true,
}

React routing with Hapi

I have a requirement of using Hapi with create-react-app where Hapi acts as a proxy for api requests and also serves the React app.
I'm trying to get the routing working, but it doesn't seem to work with the current Hapi configuration.
Here is my server code:
const Path = require('path');
const Hapi = require('hapi');
const Inert = require('inert');
const init = async () => {
const server = new Hapi.Server({
port: process.env.PORT || 5000,
routes: {
files: {
relativeTo: Path.join(__dirname, '../build')
}
}
});
await server.register(Inert);
server.route({
method: 'GET',
path: '/{param*}',
handler: {
directory: {
path: '.'
}
}
});
const options = {
ops: {
interval: 1000
},
reporters: {
myConsoleReporter: [
{
module: 'good-console',
args: [{ request: '*', response: '*' }]
},
'stdout'
]
}
};
await server.register({
plugin: require('good'),
options,
});
await server.start();
console.log('Server running at:', server.info.uri);
};
init();
The index.html file loads fine when localhost:5000 is open. I have configured a route /dashboard in the react-router part. Hitting localhost:5000/dashboard gives a 404.
Questions:
How do I configure the routes in Hapi so that React takes over the routing after the index.html is rendered?
The current server code serves the app from the build folder after the app is built. How do I configure it for hot reload without ejecting from the create-react-app
Note: The routing works when running the react app with npm start. But this is without the Hapi server running.
I'm new to using Hapi so any pointers are appreciated.
So I played around with various hapi+inert combo and this is what ended up working for me.
server.js
const Path = require('path');
const Hapi = require('hapi');
const Inert = require('inert');
const routes = require('./routes');
const init = async () => {
console.log('Routes are', routes);
const server = new Hapi.Server({
port: process.env.PORT || 5000,
routes: {
files: {
relativeTo: Path.join(__dirname, '../build')
}
}
});
await server.register(Inert);
server.route(routes);
/**
* This is required here because there are references to *.js and *.css in index.html,
* which will not be resolved if we don't match all remaining paths.
* To test it out, comment the code below and try hitting /login.
* Now that you know it doesn't work without the piece of code below,
* uncomment it.
*/
server.route({
method: 'GET',
path: '/{path*}',
handler: {
directory: {
path: '.',
redirectToSlash: true,
index: true,
}
}
});
const options = {
ops: {
interval: 1000
},
reporters: {
myConsoleReporter: [
{
module: 'good-console',
args: [{ request: '*', response: '*' }]
},
'stdout'
]
}
};
await server.register({
plugin: require('good'),
options,
});
await server.start();
console.log('Server running at:', server.info.uri);
};
init();
/routes/index.js
/**
* Use this for all paths since we just need to resolve index.html for any given path.
* react-router will take over and show the relevant component.
*
* TODO: add a 404 handler for paths not defined in react-router
*/
const fileHandler = {
handler: (req, res) => {
console.log(res.file('index.html'));
return res.file('index.html');
}
}
const routes = [
{ method: 'GET', path: '/login', config: fileHandler },
]
module.exports = routes;
The key thing to observe here is that for any named path (in this case /login) we always return the index.html file. For all other paths we tell hapi to return files from out of our build directory so that any references to *.css or *.js file in our index.hml will be resolved and we don't face a 404.
I'm not sure how react-router takes over the path resolution once index.html is loaded, but it beyond the scope of this question and is a topic of discussion for another time maybe.
As for the second question regarding hot-reload, I am still trying to figure it out. For now I am running both the hapi server and react-app independently, as I need /api for use in the react-app. Any suggestions or answers are welcome.
Here is how I did it. Tested it. Version "#hapi/hapi": "^20.0.1".
const path = require("path")
const Hapi = require('#hapi/hapi')
const Boom = require('#hapi/boom');
const server = Hapi.server({
port: 3000,
host: '0.0.0.0',
routes: {
files: {
relativeTo: path.join(__dirname, 'YOU BUILD REACT DIR')
}
}
});
(async () => {
await server.register([
require('vision'),
require('inert')
]);
server.route(
[{
method: 'GET',
path: '/{path*}',
options: {
ext: {
onPreResponse: {
method(req, h) {
//for other path prefix /Api
const isApi = req.path.substr(1)
.toLowerCase()
.trim()
.split('/')[0]
.replace(/\//g, "") === "api"
const response = req.response
if (response && req.response.output && req.response.output.statusCode === 404) {
if (isApi)
return Boom.notFound("Not Found")
return h.file('index.html');
}
return h.continue
},
}
}
},
handler: {
directory: {
path: ".",
listing: false,
index: true
}
}
},
{
method: 'GET',
path: '/Api/test',
handler: () => "OK"
}
])
await server.start();
console.log('Server running on %s', server.info.uri)
})()

React Router Async Routes and Webpack

I am trying to use the React Router Async API to achieve some code splitting.
I have Currently a main route file and a subroute:
// routes/index.js
export const createRoutes = (store) => ({
path: '/',
component: AppView,
indexRoute: {
onEnter: (nextState, transition) => {
transition('/login')
},
},
childRoutes: [
LoginRoute(store),
]
})
And then my Login Route looks like this:
//routes/Login/index.js
export default (store) => ({
path: 'login',
/* Async getComponent is only invoked when route matches */
getComponent (nextState, cb) {
/* Webpack - use 'require.ensure' to create a split point
and embed an async module loader (jsonp) when bundling */
require.ensure([], (require) => {
/* Webpack - use require callback to define
dependencies for bundling */
const requireUnauthentication = require('containers/UnauthenticatedComponent').default;
const LoginModalContainer = require('components/Login/LoginModalContainer').default;
/* Return getComponent */
cb(null, requireUnauthentication(LoginModalContainer))
/* Webpack named bundle */
}, 'login')
},
indexRoute: {
getComponent(nextState, cb){
require.ensure([], (require) => {
const LoginStart = require('components/Login/LoginStart').default;
cb(null, LoginStart);
}, 'login')
}
},
getChildRoutes: (location, cb) => {
require.ensure([], (require) => {
const routes = [
{ path: 'login-start', component: require('components/Login/LoginStart').default },
{ path: 'login-prompt', component: require('containers/LoginContainer').default },
{ path: 'phone-number', component: require('components/Register/PhonenumberSet').default }
];
cb(null, routes);
}, 'login');
}
})
The problem is when directly navigating to /login I am getting errors:
DOMLazyTree.js:62 Uncaught TypeError: Cannot read property 'replaceChild' of null
ReactDOMComponentTree.js:105 Uncaught TypeError: Cannot read property '__reactInternalInstance$nva6m7qemb9ackp2ti2ro1or' of null
I think this is related to the asynchronous route configuration, since as soon as I change something and hot reloading kicks in, everything loads fine.

Resources