Mastering React Routing

Smit DesaiSmit Desai
5 min read

Whenever a STATE VARIABLE is updated, React re-renders the entire component.

useEffect's CB function is called only when the entire Component is rendered.

The CB Fn. will be executed after every render/re-render (-)

 useEffect(()=>{
    console.log('Use Effect CB Fn. triggered!!');
  })

The CB Fn. will be executed after intial render only ([])

useEffect(()=>{
    console.log('Use Effect CB Fn. triggered!!');
  },[])

The CB Fn. will be executed only when the [dependency] is updated (& initial render obviously).

const [isSignedIn , setIsSignedIn] = useState(false);

 useEffect(()=>{
    console.log('Use Effect CB Fn. triggered!!');
  },[isSignedIn])

So whenever the isSignedIn variable is updated and the React re-renders the component. The CB Function of this useEffect will be executed

(After Initial Render -> For sure useEffect will be called)

useState

  • Always call your useState inside the component

  • It should be on the top level when the function starts (For consistency)
    Good practice to have variables at start - as JS is synchronous

  • Never create it inside if else or for loops or inside functions (Valid but avoid it - Acc. to documentation)

Ensure you are following all this and your Application will run smoothly & you won't face many errors!!

React Router Dom (v6 - Latest)

import React from "react";
import ReactDOM from "react-dom/client";
import Header from "./components/Header";
import Body from "./components/Body";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import About from "./components/About";
import Contact from "./components/Contact";
import Error from './components/Error'

const AppLayout = () => {
 return (
  <div className="app">
   <Header/>
   <Body/>
  </div>
 )
}
// createBrowserRouter takes in Array of objects
const appRouter = createBrowserRouter([
 {
  path: '/',
  element: <AppLayout/>,
  errorElement: <Error/> // If you've error -> Show Error page eg: http://localhost:1234/random-url
 }, 
 {
  path: '/about',
  element: <About/>
 }, 
 {
  path: '/contact',
  element: <Contact/>
 }
])

const root  = ReactDOM.createRoot(document.getElementById("root"));
root.render(<RouterProvider router={appRouter}/>);

React Router DOM provides us a Hook that we can use it on Error Page.

Hook Name: useRouteError - Gives all information to us about the Error occurred, when useRouteError() is called.

import { useRouteError } from "react-router-dom";

const Error = ()=>{
 const err = useRouteError(); //! A hook provided by React Router DOM which gives us all info. about the Error occured
 console.log(err);
 return(
  <div>
   <h1>Oops!!</h1>
   <h3>Something went wrong!!</h3>
   <h2>{err?.status} - {err?.statusText}</h2>
  </div>
 )
}
export default Error;

This was a very cool thing - as this hook will have too many use cases. Great Job by the React Router DOM developers as they keep their library updated!

Create Children Routes

import React from "react";
import ReactDOM from "react-dom/client";
import Header from "./components/Header";
import Body from "./components/Body";
import About from "./components/About";
import Contact from "./components/Contact";
import Error from './components/Error';
import { createBrowserRouter, RouterProvider, Outlet } from "react-router-dom";

const AppLayout = () => {
 return (
  <div className="app">
   <Header/>
   <Outlet/>
   <Footer/>
  </div>
 )
}
// createBrowserRouter takes in Array of objects
const appRouter = createBrowserRouter([
 {
  path: '/',
  element: <AppLayout/>,
  children: [
   {
   path: '/',
   element: <Body/>
   },
   {
    path: '/about',
    element: <About/>
   }, 
   {
    path: '/contact',
    element: <Contact/>
   }
  ],
  errorElement: <Error/> //If you have error -> Show Error page  eg: http://localhost:1234/random-url
 }
])

const root  = ReactDOM.createRoot(document.getElementById("root"));
root.render(<RouterProvider router={appRouter}/>);

The Outlet will be replaced :

By Body component when on '/'
By About component when on '/about/
By Contact component when on '/contact'

<Header/>
<Outlet/>
<Footer/>

And everything else will remain intact (header, footer) only the outlet will beupdated in HTMLas well

When you want to navigate to some page:**Never Use an Anchor tag <a> href and all in React
\=> Because It refreshes the whole page once navigated (we don't want that)

You can navigate to pages without reloading*A superpower given to us by React Router DOM:Link (It works exactly the same as anchor tag but without reloading) - Behind the scenes Link uses anchor tag only <a> -As browser understands only that. Link component is a wrapper over anchor tag.*

It doesn't reloads the entire page, It just refreshes/interchanges the component. That is why React Applications are known as Single Page Application (SPA)

It's a whole single component. All the Routing are just components interchanging itself. Everything is a component in React.

SPA you never change the page (1 page). It means your page never reloads itself. It just interchanges the components via Client Side Routing.

There are 2 types of Routing in web apps

1.Client Side Routing- No network calls for html. It already has the code for all. It just loads them whenever required.
2.Sever Side Routing- eg: If you go to about-us.html. It reloads the whole page. Make a network call and fetches the HTML (from server) and renders that html on a webpage.

Dynamic Routing:

const appRouter = createBrowserRouter([
 {
  path: '/',
  element: <AppLayout/>,
  children: [
   {
   path: '/',
   element: <Body/>
   },
   {
    path: '/restaurant/:resId',
    element: <ResMenu/>
   }
  ],
  errorElement: <Error/> //If you have error -> Show Error page  eg: http://localhost:1234/random-url
 }
])

When Clicked on this entire card it should navigate to this Restaurant's Menu/Details:

{filteredRes.map((restaurants) => (          
 <Link key={restaurants.info.id} to={`restaurant/${restaurants.info.id}`} style={{textDecoration: 'none', color: "#000"}}>
     <RestaurantCard resData={restaurants} />
 </Link>
))}

Now the Key Prop will be passed to Link Component instead of RestaurantCard as now Link is the Parent

Fetching Restaurant Menu and Details using Restaurant Id passed in Params

FYI: Used useParams() hook provided by React Router DOM. Which gives us the params

SWIGGY_RES_URL And then add resId in the end. (Pizza Hut id: 9867)

ResMenu.component.ts

import { useEffect, useState } from "react";
import Shimmer from "./Shimmer";
import { useParams } from "react-router-dom";
import { SWIGGY_RES_URL } from "../utils/constants";

const ResMenu = () => {

 const [resInfo, setResInfo] = useState(null);

 useEffect(()=>{
  fetchMenu();
 },[]) // This Callback Fn. will be executed only once - after the inital render

 const {resId} = useParams();

 const fetchMenu = async ()=>{
  const data = await fetch(`${SWIGGY_RES_URL}${resId.toString()}`);

  const json = await data?.json();
  setResInfo(json?.data)
 }

 if(resInfo === null) return <Shimmer/>;

 const { name, cuisines } = resInfo?.cards[2]?.card?.card?.info;
 const {itemCards} = resInfo?.cards[4]?.groupedCard....;

  return (
    <div className="menu">
      <h1>{name}</h1>
      <p>{cuisines}</p>
      <h2>Menu</h2>
      <ul>
        {itemCards.map((item)=> <li key={item.card.info.id}>{item.card.info.name} -
         {"Rs."} {item.card.info.price / 100 || item.card.info.defaultPrice / 100}</li>)}
   {/*  <li>{itemCards[0].card.info.name}</li>
        <li>{itemCards[1].card.info.name}</li>
        <li>{itemCards[2].card.info.name}</li>  */}
      </ul>
    </div>
  );
};

export default ResMenu;

GraphQL usage: Eg: Swiggy Backend API is giving us tons of data but we are hardly using very less amount of data on your webpage. So GraphQL helps us in this case
Helps you dealing with over-fetching. So**Load only data which is required in your app. - Major Use

Stay Tuned for next Blogs!!

0
Subscribe to my newsletter

Read articles from Smit Desai directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Smit Desai
Smit Desai