skip to Main Content

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


  1. The problem is unrelated to the react-router This line

    const [screenLoading, setScreenLoading] = useState(false);
    

    It’s invalid, you can’t use useState outside of a component body. Same with useEffect

    Create a component to fix that:

    const Wrapper = () => {
      const [screenLoading, setScreenLoading] = useState(false);
    useEffect(() => {
      setScreenLoading(true);
      setTimeout(() => {
        setScreenLoading(false);
      }, 1000);
    }, []);
    
      return <div className='layout'>
        {screenLoading? <Load /> :
        <RouterProvider router={router} />}
      </div>
    }
    
    ReactDOM.createRoot(document.getElementById('root')).render(<Wrapper />)
    
    Login or Signup to reply.
  2. 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.

    If you are not server rendering your app, createBrowserRouter will
    initiate all matching route loaders when it mounts. During this time,
    you can provide a fallbackElement to give the user some indication
    that the app is working. Make that static hosting TTFB count!

    ReactDOM.createRoot(document.getElementById('root')).render(
      <div className='layout'>
        <RouterProvider router={router} fallbackElement={<Load />} />
      </div>
    );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search