I’m trying to add a pre-loader in my personal site, and the problem is that is cannot make it work. As you can see in image
I’m try to render it before router so that it runs before anything else.
code is simple
const [screenLoading, setScreenLoading] = useState(false);
useEffect(() => {
setScreenLoading(true);
setTimeout(() => {
setScreenLoading(false);
}, 1000);
}, []);
after a sec state will go false and site will load. but I am getting this error "Warning: Invalid hook call. Hooks can only be called inside of the body of a function component." which makes sense, but I don’t have any return function to run the code inside.
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { useState, useEffect } from 'react';
//contexts
// import { MousePos } from './mousePosContext';
//components
import Header from './Header'
// import Footer from './Footer'
import ErrorPage from "./error-page";
//pages
import Home from './Home'
import Profile from "./Profile"
import Works from "./Works"
import Blogs from "./Blogs"
import Wm from "./works/web-monitization"
import Ss from "./works/scheduling-system"
import Bs from "./works/bharatSteel"
import Load from "./loader"
const [screenLoading, setScreenLoading] = useState(false);
useEffect(() => {
setScreenLoading(true);
setTimeout(() => {
setScreenLoading(false);
}, 1000);
}, []);
const router = createBrowserRouter([
{
path: "/",
element: <Header />,
errorElement: <ErrorPage />,
children: [
{
path: "",
element: <Home />,
index: true,
loader: async () => {
console.log("router loader called")
const test = (x) => {
console.log(x)
}
return test
}
},
{
path: "/profile",
element: <Profile />,
},
{
path: "/works",
element: <Works />,
},
{
path: "/works/web-monetization",
element: <Wm />
},
{
path: "works/input-validaton",
element: <Wm />
},
{
path: "works/scheduling-system",
element: <Ss proji="scheduling-system" />
},
{
path: "works/bharat-steel",
element: <Bs proji="scheduling-system" />
},
{
path: "blogs",
element: <Blogs />
},
{
path: "loader",
element: <Load />
}
]
}
]);
ReactDOM.createRoot(document.getElementById('root')).render(
<div className='layout'>
{screenLoading
? <Load />
: <RouterProvider router={router} />
}
</div>
)
So can someone tell me how to implement it, and if there is any other way to implement preloader with React-Router? The loader needs to run every time site loads for first time regard less of which page is being loaded.
2
Answers
The problem is unrelated to the react-router This line
It’s invalid, you can’t use
useState
outside of a component body. Same withuseEffect
Create a component to fix that:
Issue
The issue here is that you are using React hooks outside any React component. This breaks the Rules of Hooks.
Solution
Konrad’s answer provides a solution to allow you to use the React hooks and correctly conditionally render your loading UI.
I don’t think you need to re-invent the wheel though, the
RouterProvider
component has a fallbackElement prop to render while a route is loading.