NextJs router seems very slow compare to React Router - javascript

I have a website built in React Js and the same one on Next Js as well.
The problem which I am facing right now is, the router seems very slow in the nextJs compare to react-router-dom, It's taking almost 2-3 seconds to change the route.
Here are the URLs where you can feel the difference between the performance by moving around different pages.
https://cutt.ly/mhbPkOE (React Router Dom) vs
https://cutt.ly/BhbPvHv (NextJs)
I had read some comments on Github where few experts are saying that It will resolve in production. but It looks same in production too.
Please have a look at the following code
_app.jsx
// import App from 'next/app'
import React from "react"
import Router from 'next/router';
import "../static/sass/application.scss";
import "bootstrap/dist/css/bootstrap.min.css";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import 'semantic-ui-css/semantic.min.css'
import { wrapper } from "../../redux/utils/store"
import App from 'next/app';
// A simple component that we created
import {LoaderOverlay} from '../components/Reusable'
class MyApp extends App {
constructor(props){
super(props)
this.state = {
isLoading: false,
}
Router.onRouteChangeStart = (url) => {
// Some page has started loading
this.setState({
isLoading: true,
}) // set state to pass to loader prop
};
Router.onRouteChangeComplete = (url) => {
// Some page has finished loading
this.setState({
isLoading: false,
}) // set state to pass to loader prop
};
Router.onRouteChangeError = (err, url) => {
this.setState({isLoading: false,})
};
};
render() {
const {Component, pageProps} = this.props
return (
<div>
{this.state.isLoading ? (
<LoaderOverlay/>
) : (
<Component {...pageProps} />
)}
</div>
)
}
}
export default wrapper.withRedux(MyApp);
_document.jsx
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const originalRenderPage = ctx.renderPage
ctx.renderPage = () =>
originalRenderPage({
// useful for wrapping the whole react tree
enhanceApp: (App) => App,
// useful for wrapping in a per-page basis
enhanceComponent: (Component) => Component,
})
// Run the parent `getInitialProps`, it now includes the custom `renderPage`
const initialProps = await Document.getInitialProps(ctx)
return initialProps
}
render() {
return (
<Html lang="en">
<Head>
<link async rel="stylesheet" href="//cdn.jsdelivr.net/npm/semantic-ui#2.4.1/dist/semantic.min.css"/>
</Head>
<body>
<div className={'main-wrapper'}>
<Main />
</div>
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

Development mode (next dev) is much slower because the routes aren't pre-built.
All delay related to routing assuming you don't have any server side blocking data requirements via getInitialProps, getServerSideProps, should not be present when running production mode with next build followed by next start.

Not sure if you have found a fix for this yet, but I came across this article about "shallow routing". I can't see much improvement in my application when using it, but maybe it will help someone else:
https://nextjs.org/docs/routing/shallow-routing

Hey I think you are in your production mode.
That's why it is slow. But if you will host your site it will be pretty much like react only.
But then also if you want to routing fast
Then npm i next#4.2.3 --save will work fine..

to solve this issue followed the commands:
yarn build/nmp build
yarn start/npm start
I hope this will solve this issue

Related

Designing persistent layouts in Next.js

I'm going through this article and I'm trying to figure out how the persistence is supposed to occur in Option 4. From what I can tell, you'd need to redefine the .getLayout for every page. I'm not sure how the logic for nesting is incorporated into further urls.
Here's the code from the article
// /pages/account-settings/basic-information.js
import SiteLayout from '../../components/SiteLayout'
import AccountSettingsLayout from '../../components/AccountSettingsLayout'
const AccountSettingsBasicInformation = () => <div>{/* ... */}</div>
AccountSettingsBasicInformation.getLayout = page => (
<SiteLayout>
<AccountSettingsLayout>{page}</AccountSettingsLayout>
</SiteLayout>
)
export default AccountSettingsBasicInformation
// /pages/_app.js
import React from 'react'
import App from 'next/app'
class MyApp extends App {
render() {
const { Component, pageProps, router } = this.props
const getLayout = Component.getLayout || (page => page)
return getLayout(<Component {...pageProps}></Component>)
}
}
export default MyApp
For example, say AccountSettingsBasicInformation.getLayout is /settings/, how would I use this template to produce something at /settings/username
P.S. If someone has done something in the past they'd recommend over this, I'm open to ideas.
Yes, you have to redefine the getLayout function to every page. As long as the SiteLayout component stays “unchanged” (eg.no props change) the rendered content in that layout component (not the page content itself) stays persistent. This is because React wont rerender that component.
I used Adam’s article when I was building next.js lib for handlin modal routes. You can check the example folder where you can see I am defining the getLayout property on every page which should be rendered with layout.
Example: https://github.com/svobik7/next-bodies/tree/master/example

How can I import component from variable or props - Dynamic router

Context for a good solution (short question in bottom).
I have a database (API) which generate list of data of apps like AppA, AppB, AppC, etc. with their name, path...
With a map, I generate (react router) links to these apps based on this data list (in the main <App/>) and front.
With another identical map (below), I have made the router which should call the App based on the route and app name:
function(Router) {
const [routes, setRoutes] = useState([]);
useEffect(() => {
fetch("MyAPI")
.then(res => res.json())
.then((result) => {setRoutes(result.results)}
)
}, [])
// The route for each app and after the map, the route to the home with these links
return (
<Switch>{
routes.map(result =>
<Route exact path={"/"+result.route} key={keygen(16)}>
<AppCaller name={result.name} id={result.id} path={"/"+result.route}/>
</Route>
)}
<Route exact path="/">
<App />
</Route>
</Switch>
)
}
export default Router
My first problem is I cannot neither give a component Name like <result.name/> from the API to call this component in the Router nor import dynamically this component.
My first solution was to create another component <AppCaller/> with the name and path as Props to remove the component problem like this :
import React from "react";
import Window from "./dashComponents"
import subApp from "./Apps/Store"
class AppCaller extends React.Component {
render() {
return (
<Window name={this.props.name}>
<subApp/>
</Window>
)
}
}
export default AppCaller
In fact, I can keep the <subApp/> name even if the app is different. The only thing i need to change is the "where" is the app comes from.
Begin of short question
So, How can I change the path of the import statement to "import" the good App in this dynamic component ? (with a Props in this case)
import subApp from this.props.path
This may looks like this (if it was static):
import subApp from "./Apps/App1" in the App1 file
import subApp from "./Apps/App1" in the App2 file etc.
Another idea seen in React Documentation and in Stack Overflow is to use Lazy Import but it does not works:
import React, {Suspense} from "react";
import Window from "./dashComponents"
//import subApp from "./Apps/Store"
const path = this.props.path
let name = this.props.name
function AppCaller() {
const SubApp = React.lazy(() => import('./Apps/'+name))
return (
<Window name={name}>
<Suspense fallback={<h2>"Chargement..."</h2>}>
<SubApp/>
</Suspense>
</Window>
)
}
export default AppCaller
OR
class AppCaller extends React.Component {
render() {
const SubApp = React.lazy(() => import('./Apps/'+this.props.name));
return (
<Window name={this.props.name}>
<Suspense fallback={"Chargement..."}>
<SubApp/>
</Suspense>
</Window>
)
}
}
export default AppCaller
"The above error occured in one of your React Component..."
Thank you for your help.
I Try to be very accurate and find everywhere. So please, be free to tell me precision before judging.
Edit 1 with full Error
I have this error when I click in one of the links generated.
The above error occurred in one of your React components:
in Unknown (created by AppCaller)
in Suspense (created by AppCaller)
in div (created by Window)
in Window (created by AppCaller)
in AppCaller (created by RoutesX)
in Route (created by RoutesX)
in Switch (created by RoutesX)
in RoutesX
in div
in Router (created by BrowserRouter)
in BrowserRouter
Consider adding an error boundary to your tree to customize error handling behavior.
Visit 'Facebook' to learn more about error boundaries.
Edit 2 with initial Error
react-dom.development.js:11865 Uncaught ChunkLoadError: Loading chunk 0 failed.
(error: http://dash.localhost:8000/0.main.js)
at Function.requireEnsure [as e] (http://dash.localhost:8000/static/frontend/main.js:106:26)
at eval (webpack:///./src/components/AppCaller.js?:45:36)
at initializeLazyComponentType (webpack:///./node_modules/react-dom/cjs/react-dom.development.js?:1432:20)
at readLazyComponentType (webpack:///./node_modules/react-dom/cjs/react-dom.development.js?:11862:3)
...
requireEnsure # main.js:106
eval # AppCaller.js:45
...
And
0.main.js:1 Failed to load resource: the server responded with a status of 404 (Not Found)
Ok Everybody !
So, it was not a React but a Webpack problem !
When you use a React lazy to import specific component, and you "compile" with npm run dev (or build) webpack split the code by Chunck but the configuration file is wrong.
Here's my configuration file which works:
module.exports = {
output: {
filename: 'App.js',
publicPath: '/static/frontend/',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
};
I needed to add the "public path" to the path of the "chunck' scripts. :)

Why does React fail to define 'App' component?

I made simple Reactjs that has index.js and App.js. App.js defines App component. index.js imports App.js and uses App component. However, index.js receives "App is undefined error". How can this be?
index.js:
import React from "react"
import '../components/App.js';
export default class Home extends React.Component {
render() {
return (
<div>
<p>Welcome to donghwankim.com!</p>
<p>Powered by Gatsby</p>
<App />
</div>
)}
}
It imports App.js:
/*global kakao*/
import React, { Component } from 'react';
import '../css/App.css';
class App extends Component {
componentDidMount() {
const script = document.createElement('script');
script.async = true;
script.src = "https://dapi.kakao.com/v2/maps/sdk.js?appkey=ee494d4410cf578c0566203d2f487eb9";
document.head.appendChild(script);
script.onload = () => {
kakao.maps.load(() => {
let el = document.getElementById('map');
let map = new kakao.maps.Map(el, {
center: new kakao.maps.Coords(523951.25, 1085073.75)
});
});
};
}
render() {
return (
<div className="App" id="map"></div>
);
}
}
export default App;
and it gives the following error during development:
12:8 error 'App' is not defined react/jsx-no-undef
From what I learned, shouldn't imported App.js define App? Why is it undefined?
Thank you.
This line:
import '../components/App.js';
...doesn't actually import anything, it just runs the code in App.js. To import the default export, you do so explicitly:
import App from '../components/App.js';
// ^^^^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−−
You may now be wondering: "But why doesn't importing App.js and running its code create App automatically?" Because modules don't create globals (not unless you go out of your way to do so)., and there are no automatic imports. One of the key aspects of modules is that you explicitly define the links between them. One benefit of doing that is that when you're using a library with lots of functionality exported by a single module, the browser (or your bundler) can tree-shake it — keeping only the parts you actually use, and discarding the rest (the dead wood that fell out when it shook the tree).

ReactGA.initialize must be called first

I am getting the following warning multiple times (for multiple pages) despite me initializing at the root of my app. This makes me wonder if google analytics is even working properly?
[react-ga] ReactGA.initialize must be called first or GoogleAnalytics should be loaded manually
I am using ReactGA to handle my google analytics tags, and I cannot get it to work. According to the documentation and a handful of other questions about this online, all I need to do is insert this at my application root:
App.js:
import ReactGA from 'react-ga';
ReactGA.initialize('G-xxxxxxxxxx');
const app = () => (
// Root level components here, like routing and navigation
)
I am using Server Side Rendering, so I am making sure the window object exists before tracking. This line is put at the end of my imports on each of my pages:
example page.js:
import ReactGA from 'react-ga';
if (typeof(window) !== 'undefined') {
ReactGA.pageview(window.location.pathname + window.location.search);
}
function page() {
return(<div className="page">Hello, World</div>)
}
export default page;
At this point, there isn't a lot of information on how to set up Google Analytics for SSR applications, so I'm not entirely sure what I need to do to get this working. Any help is appreciated!
I finally found the solution after a lot of tinkering with potential solutions. Since pageview was being fired before initialize could finish, I tired delaying pageview as much as I could by placing it in componentDidMount(). That implementation looks like this:
App.js:
//imports
import ReactGA from 'react-ga';
ReactGA.initialize('UA-xxxxxxxxx-x');
const App = () => (
<div className="App">
<Navigation />
<AppRouting />
</div>
);
Page.js:
import ReactGA from 'react-ga';
class MyPage extends React.Component {
componentDidMount() {
ReactGA.pageview(window.location.pathname + window.location.search);
}
render() {
return(
<Component />
);
}
}
In functional components the same can be done using the useEffect hook
useEffect(() => {
ReactGA.pageview(window.location.pathname + window.location.search);
}, ['your dep'])

Minimal `_app.js` replacement without changing base behavior

According to next.js documentation, if you want to customize <App>, you have to create a pages/_app.js page, then put your modifications inside.
Still, in their example there is some code, and I don't know what's its purpose:
import React from 'react'
import App, { Container } from 'next/app'
export default class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
render () {
const { Component, pageProps } = this.props
return (
<Container>
<Component {...pageProps} />
</Container>
)
}
}
Is this the minimal form? Does this example changes the initial behavior?
In other words, is this code sufficient to extends the original app:
import React from 'react'
import App from 'next/app'
export default class MyApp extends App {}
Yes, what you have there won't change anything and is the minimum to extend App (I've tested this).
I think the reason they've included the overridden getInitialProps and render methods in the documentation is because these are likely the places that you'd want to add customizations to and the code in these is needed if you are overriding them.
For example, if you override getInitialProps but don't return the result of Component.getInitialProps(ctx) (in this case Component is the current page component, like ./pages/index.js) then your page components won't have initial props set.

Categories

Resources