React_07

React_07

In this blog, we'll implement routing in our Foodhouse app. We will be learning about the types of routing, dynamic routing, SPA and much more.

What are various ways to add images into our App?

  • Using the full URL of the image for the web (CDN) or any public images. Example :
<img src="https://reactjs.org/logo-og.png" alt="React Image" />
  • Adding the image into the project Drag your image into your project and import it into the desired component.
import reactLogo from "./reactLogo.png";
export default function App() {
  return <img src={reactLogo} alt="react logo" />
}
  • The correct way to structure images in your project is to add them in an images folder. If you are using other assets than just images, you might want to add all in the assets folders.
import reactLogo from "../../assets/images/reactLogo.png";
export default function App() {
  return <img src={reactLogo} alt="react logo" />
}

What would happen if we do console.log(useState())?

If we do console.log(useState()), we get an array [undefined, function] where first item in an array is state is undefined and the second item in an array is setState function is bound dispatchSetState.

What is SPA?

Single Page Application (SPA) is a web application that dynamically updates the webpage with data from web server without reloading/refreshing the entire page. All the HTML, CSS, JS are retrieved in the initial load and other data/resources can be loaded dynamically whenever required. An SPA is sometimes referred to as a single-page interface (SPI).

What is difference between Client Side Routing and Server Side Routing?

In Server-side routing or rendering (SSR), every change in URL, http request is made to server to fetch the webpage, and replace the current webpage with the older one.

In Client-side routing or rendering (CSR), during the first load, the webapp is loaded from server to client, after which whenever there is a change in URL, the router library navigates the user to the new page without sending any request to backend. All Single Page Applications uses client-side routing.

react-router-dom

Speaking of routing, we will be using the react-router-dom library, which is an open-source library developed by Remix, not Facebook, mind you! for all our routing inside our React application.

Install it using the following command:

npm install react-router-dom

createBrowserRouter

It is a function provided by the react-router-dom library which takes in an array of configuration objects, which themselves take configurations like path, element, errorElement and children. It return a router. It is the most recommended router while using this library.

RouterProvider

Earlier we used to directly render the AppLayout inside our root, but now since we are routing into different pages based on the specified URL path, we will have to provide our router so that it can be rendered inside root. RouterProvider is a component provided by the react-router-dom library for this very purpose.

Outlet

Outlet is a component provided by the react-router-dom library that acts as an outlet for rendering different components in its place, depending on the specified path mentioned in the children configuration of our router.

// App.js
import { createBrowserRouter, RouterProvider, Outlet } from "react-router-dom"; 
import Error from "./components/Error";

const AppLayout = () => {
  return (
    <React.Fragment>
      <Header />
      <Outlet />
      <Footer />
    </React.Fragment>
  );
};

const appRouter = createBrowserRouter([
  {
    path: "/",
    element: <AppLayout />,
    errorElement: <Error />,
    children: [
      {
        path: "/",
        element: <Body />,
      },
      {
        path: "/about",
        element: <About />,
      },
      {
        path: "/contact",
        element: <Contact />,
      },
      {
        path: "/restaurant/:resId",
        element: <RestaurantMenu />,
      },
    ],
  },
  ,
]);

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

useRouteError

useRouteError is a hook provided to us by the react-router-dom library which returns an error object that contains a lot of information about the routing error like the status code, error message and much more.

We can specify when to use the below error component in the errorElement property in our router configurations.

// Error.jsx
import { useRouteError } from "react-router-dom";

const Error = () => {
  const { status, statusText } = useRouteError();

  return (
    <div>
      <h1>Oops!!</h1>
      <h2>Something went wrong...</h2>
      <h2>{status + " : " + statusText}</h2>
    </div>
  );
};

export default Error;

We can seperately create and use About and Contact components in place of Body component depending upon the specified URL path.

In this way, if we change any header tabs like Home, About, Contact, the entire page does not reload, the Header and Footer stays, only the main content between the header and footer changes depending on the selected header tab without any page reload. This is the core principle of building single page applications.

If we use an anchor tag with href attribute for routing, then the page reloads. To avoid page reload, the react-router-dom library provides us with Link component which takes in to attribute instead of the href attribute.

// Header.jsx
import { Link } from "react-router-dom";

const Header = () => {
  {/* some logic */}
  return (
    <div className="header">
      <Title />
      <div className="nav-items">
        <ul>
          <li>
            <Link to="/">Hom</Link>e
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/contact">Contact</Link>
          </li>
          {/* remaining logic */}
        </ul>
      </div>
    </div>
  );
};

Dynamic Routing

Let's say we want to dynamically render the menu and restaurant information in a seperate page dedicated to any restaurant. We can generalise the route path with some restaurantId resId for that restaurant this way:

{
    path: "/restaurant/:resId",
    element: <RestaurantMenu />,
}

You can simply put the restaurant cards inside a Link component such that they route to the particular restaurant page with the above path.

Here is a simple code for restaurant menu component:

// Restaurant.jsx
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import Shimmer from "./cards/Shimmer";
import { menu_api_URL, IMG_CDN_URL } from "../config";

const RestaurantMenu = () => {
  const { resId } = useParams();
  const [restaurant, setRestaurant] = useState(null);
  const [menu, setMenu] = useState([]);

  useEffect(() => {
    getRestaurantInfo();
  }, []);

  async function getRestaurantInfo() {
    try {
      const data = await fetch(menu_api_URL + resId.toString());
      const json = await data.json();
      const restaurantData = json?.data?.cards
        ?.filter(
          (card) =>
            card?.card?.card["@type"] ===
            "type.googleapis.com/swiggy.presentation.food.v2.Restaurant"
        )
        .map((card) => card?.card?.card?.info)[0];
      const menuData = json?.data?.cards
        ?.filter((card) => card?.hasOwnProperty("groupedCard"))[0]
        ?.groupedCard?.cardGroupMap?.REGULAR?.cards?.filter(
          (card) =>
            card?.card?.card["@type"] ===
            "type.googleapis.com/swiggy.presentation.food.v2.ItemCategory"
        )
        ?.map((card) => {
          return {
            title: card?.card?.card?.title,
            items: card?.card?.card?.itemCards,
          };
        });
      setRestaurant(restaurantData);
      setMenu(menuData);
    } catch (error) {
      console.log(error);
    }
  }

  return !restaurant ? (
    <Shimmer />
  ) : (
    <div className="restaurant-menu-container">
      <div>
        <h2>{restaurant?.name}</h2>
        <img
          src={IMG_CDN_URL + restaurant?.cloudinaryImageId}
          alt="Restaurant Image"
        />
        <h3>{restaurant?.areaName}</h3>
        <h3>{restaurant?.city}</h3>
        <h3>{restaurant?.avgRating} stars</h3>
        <h3>{restaurant?.costForTwoMessage}</h3>
      </div>
      <div>
        <h1>Menu</h1>
        {menu &&
          menu?.map((category, categoryIndex) => (
            <div key={categoryIndex}>
              <h2>{category?.title}</h2>
              <ol>
                {category?.items?.map((item, itemIndex) => (
                  <li key={`${itemIndex} - ${item?.card?.info?.id}`}>
                    <p>
                      <strong>{item?.card?.info?.name}</strong>&#8377;{" "}
                      {item?.card?.info?.price / 100}
                    </p>
                    <p>{item?.card?.info?.description}</p>
                  </li>
                ))}
              </ol>
            </div>
          ))}
      </div>
    </div>
  );
};

export default RestaurantMenu;

Here is a sample of how the code would look functionally, ignore the CSS, always remember we are learning React 🤣

This is live data from the publicly available Swiggy API now integrated into our Foodhouse app.

That's all for now folks. See you in the next blog!