Lazy load components in react - javascript

I'm working on a legacy react-app, hence lot of pieces cannot be reasoned about but simply accepted.
So, I have a couple of components that load a lot of dependencies and are obviously not important for the first render. Hence, I tried the following:
const HeavyComp = lazy(() => import("HeavyComponent.jsx");
function Home() {
return <div>
<HeavyComp />
</div>
}
As a result of this, HeavyComponent is loaded as part of main bundle and but is only visible after that component is loaded. This helps by breaking scripting time but FCP is still far away.
So, I tried the following:
function Home() {
const [ heavyComponent. setHeavyComponent ] = useState(null);
useEffect(() => {
setHeavyComponent(
lazy(() => import("HeavyComponent.jsx")
);
}, []);
return <div>
{
heavyComponent && <heavyComponent />
}
</div>
}
I thought this'd help but same as before, FCP was still delayed until heavyComponent was downloaded, parsed and rendered. So my only option was to make it async using setTimeout or better requestIdleCallback.
Is this the best solution or is there something better?

Assuming that with FCP you are referring to "first content paint". The best option is to use the Suspense component. With it, you can add a fallback loader component (<Spinner /> in this example).
import { Suspense, lazy } from 'react';
const HeavyComp = lazy(() => import("HeavyComponent.jsx");
function Home() {
return <div>
<Suspense fallback={<Spinner />}>
<HeavyComp />
</Suspense>
</div>
}
React concurrent-mode documentation

Related

Loading page on nextjs 13

Hi im trying to get a loading page to show while website is taking the time to load. as it quite a large website I thought a loading screen would provide the best possible user experience however I cannot seem to figure out how to get it to work on nextjs 13. I have created a simple functional component that says loading... and have imported it directly into my layout.jsx folder.
I am using the app directory method which is quite new and im also new at nextjs so im a little lost ^^
I imagine I might need to set state at some point but I cant seem to figure out when and where to do it
any advice would be great.
thanks
import "./globals.css";
import React, { useState, useEffect } from "react";
import Loading from "../components/loading/loading";
const Layout = ({ children, dataLoaded }) => {
const [loading, setLoading] = useState(true);
useEffect(() => {
if (dataLoaded) {
setLoading(false);
}
}, [dataLoaded]);
return (
<body className="app {oswald.className}">
{loading && <Loading />}
{children}
</body>
);
};
export default Layout;
.
.
.
Attempt 1 -
After following one of the answers below it does not seem like my loading page is showing up at all. and no errors showing up.
my layout is as follows
layout.jsx
import "./globals.css";
import { Suspense } from "react";
import Loading from "../components/loading/loading";
export default function RootLayout({ children }) {
return (
<html lang="en">
<head />
<body>
<Suspense fallback={<Loading />}>{children}</Suspense>
</body>
</html>
);
}
LoadingPage.js
const LoadingPage = () => {
return (
<div className="loading w-screen h-screen bg-red-100">
<p>Loading...</p>
</div>
);
};
export default LoadingPage;
Loading.js
import LoadingPage from "#/components/loading/loading";
export default function Loading() {
return <LoadingPage />;
}
In NextJS 13, there's actually a default way to handle loading states within pages. You can declare a loading.tsx file in your /app directory, with this content:
export default function Loading() {
return <Loading />
}
Then, inside your Layout, you can wrap your page with a Suspense tag, like this:
<Layout>
<Navbar>
...
<Suspense fallback={<Loading/>}>
<Page/>
</Suspense>
</Layout>
Your loading state will be automatically handled upon navigation.

Why My React Component Render Multiple Times In Console?

Im new in react.
I'm Created two file App.js and UseEffect.js
I'm Learn about lifecycle in react with function.
So When I See in console, that's render multiple time.
You can see my picture below.
My Console In Browser
This Is My Code
UseEffect.js
import React, {useState, useEffect} from "react";
function MyFunction(){
console.log('-> Function Init')
const [count, setCount] = useState(0)
const handleCount = () => {
setCount(prevState => {
return prevState+1
})
}
//LifeCycle
useEffect(() => {
console.log('my first effect')
})
console.log(`-> Start Render (${count})`)
return(
<div>
<h1>Function Component</h1>
<p>
<button onClick={handleCount}>Count</button>
{count}
</p>
</div>
)}
export default MyFunction
App.Js
import './App.css';
import UseEffect from './components/UseEffect'
function App() {
return (
<div className="App">
<UseEffect />
</div>
);
}
export default App;
How do it's work?, I Want it. it's just render one times.
Your useEffect call is missing a dependency array. When you want it to run only at the initial render, you need to pass it an empty array as its dependencies.
useEffect(() => {
console.log('my first effect')
}, [])
For further details, see this question.
Why it renders twice:
It's an intentional feature of the StrictMode. This only happens in development, and helps find accidental side effects put into the render phase. We only do this for components with Hooks because those are more likely to accidentally have side effects in the wrong place.
-gaearon
TLDR: It's a feature not a bug.

React scroll page to the top after transition

I want my application to go to the top of the page when another page within the site is clicked. I have used the code from: https://v5.reactrouter.com/web/guides/scroll-restoration and put in a separate file.
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
What I have tried:
I have placed <ScrollToTop /> before <Switch /> and after <BrowserRouter />as in the docs (these are in separate files).
Unlike the docs, the <App /> in my application is in a separate file, so <ScrollToTop /> is not placed near it.
When I console.log, I see it being hit at the correct instance (when there is a page change), but nothing occurs. I have also looked into many stackoverflow posts concerning the same issue, but I could not incorporate their suggestions.
What are possible issues:
Calling window.scrollTo(0, 0) outside the function does not seem to work. Are there any alternatives to this function?
Is the placement of <ScrollToTop /> incorrect? Does it have to be called near the <App />?
Welcome to Stackoverflow #user19251203
You have already gone half the way. You really don't need to create a component for it. You can create a custom hook and place it in your <App /> and then call it:
function App() {
// Scroll to top on every transition custom hook
const useScrollToTop = () => {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
};
useScrollToTop();
return (
<div className="App">
.......
</div>
)
}
This way whenever you go to a new page, it scrolls to the top of the page.

Individual loading animation for each page with Next.js

I want each of my pages to have different loading animations when loading. How can i achieve this?
It is not possible to put the loading component on the page component like this:
//Page component
Page.Loader = SomeLoaderComponent
//_app.tsx
const Loader = Component.Loader || DefaultLoader;
This will not work because "Component(the page)" isnt loaded/rendered yet.
I have also tried dynamic import with next.js, so that i can import the correct page based on the url, and then get the correct loader. My initial plan was to add the Loader to the page component, as shown at the first line in the code above. That does not work because I have to give an explicit path.
const getLoader = (pagePath: string): ComponentType => {
const Loader = dynamic(() => import(pagePath));
return Page.Loader;
};
This is stated in the Next.js docs:
So the question is: How can I get a custom loader per page?
You can use Suspense and lazy to accomplish your task.
lets say you have ComponentA.js as follows
const ComponentA = () => {
return <div>Helloooo</div>
}
You can create another component WrapperA.js file
import React, { Suspense } from 'react';
const WrapperA = React.lazy(() => import('./ComponentA'));
function WrapperA() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<ComponentA />
</Suspense>
</div>
);
}
in the place of <div>Loading...</div> you can have any loader component you like. and export WrapperA to your routes or tab as needed.

How to call returned const from a react function in a class based component

Here I have a Loading screen as a functional react component that I try to render conditionally in the App component.
The concept of this loading screen is that I have a boolean variable that will be used to conditionally render the home page after the loading screen ends.
import React from 'react';
import { useState, useEffect } from 'react';
import { useSpring, animated } from 'react-spring';
import BarLoader from 'react-spinners/BarLoader';
import Logo from "../assets/images/logo.svg";
const LoadingScreen = () => {
const spinner = `
display: block;
margin: 0 auto;
width: 150px;
height: 2.5px;
`;
const style = useSpring({opacity: 1, from: {opacity: 0}});
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
setTimeout(() => {
setIsLoading(false)
}, 4000)
}, [])
const LoadingTemplate = () => {
<animated.div className="loading-screen" style={style}>
<div className="loader-wrapper">
<img className="splash-logo" src={Logo} alt="Marouane Edghoughi" />
<BarLoader color="#384BEB" css={ spinner } loading={isLoading} />
</div>
</animated.div>
}
return { LoadingTemplate, isLoading }
}
export default LoadingScreen;
When I try to call the boolean variable and the screen template in the following code:
render() {
const {LoadingTemplate, isLoading} = LoadingScreen();
return (
<Router>
{
isLoading ?
<LoadingTemplate />
:
<Container className="theme p-0" fluid={true}>
{/* something will be displayed here */}
</Container>
}
</Router>
);
}
}
I just get this error:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
The function is working properly if I try to call it from a functional component. This is my first time trying it with a class.
Any help would be greatly appreciated ^_^
The error message is 100% correct. Hooks can be used only in Function Components, and cannot be used like this in class components. The underlying mechanics of the two types are different. Hooks are a feature of functional components and rely on those mechanics, and are therefore not compatible with class components.
You may not realize that you are using a hook, but LoadingScreen actually is one: It returns a value other than a React Element, and you are calling it as a function (i.e. const x = LoadingScreen()), rather than using it as a component (i.e. <LoadingScreen />).
That's definitely not allowed in class components. You could use a function component instead:
const Component = () => {
const {LoadingTemplate, isLoading} = LoadingScreen();
return (
<Router>
{
isLoading ?
<LoadingTemplate />
:
<Container className="theme p-0" fluid={true}>
{/* something will be displayed here */}
</Container>
}
</Router>
);
}
}
Or you can try these methods to get around this limitation. If you do decide to use a function component instead, then you should use useLoadingScreen to follow the React hook naming conventions.

Categories

Resources