React Exploring the World

React Exploring the World

Micro-services v/s Monolith

Monolith: A huge big project which itself has API code, UI code, Auth Code, DB code, etc. Everything inside a single project. Building & compiling this project itself is huge task here. All developers work on same project/repo. Everything with single language.

In today's time We use Micro-service Architecture

Micro-services: In this we prefer to have a separate project for everything. eg: UI Service, Backend service(Handling API), DB, Auth, SMS Service - everything is separate
This is known as Separation of concerns.
With Micro services all teams work on different repo/project and they've their own deployment cycle.
You can have different Tech stack for different services.

All these services talk to each other.
As all the services are deployed & are running on their specific ports.
And at the end of the day all these ports are mapped to DOMAIN name

:1234 - UI service running - Deployed on "/"
:1000 - Backend service - Deployed on "/api"
:3000 - SMS service - Deployed on "/sms"

**To interact with different services - They make a call to different urls or ports

Let's how a React Application talk to different services outside of it's world
(eg: make a backend API call & fetch data)***

We are going to use the second Approach. As user won't have to see an empty screen until the API response is received.

*So How Can we do that? -> By using useEffect Hook
*

**useEffect takes in 2 arguments: 1.) A Callback Function & 2.) Dependency array
**

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

**Once the entire component is rendered then only this useEffect's Callback Function will be called.
**As soon as the render cycle is finished it will quickly call the useEffect's Callback Function.

import RestaurantCard from "./RestaurantCard";
import { RES_LIST } from "../utils/mockData";
import { useEffect, useState } from "react";


const ResNearYou =()=>{
  const [listOfRes, setListOfRes] = useState(RES_LIST);

  useEffect(()=>{
    console.log("Use Effect Callback function Called!");
  },[])

  console.log("Component Rendered!");

  return (
    <div className="res-near">
      <h1>Restaurants near you</h1>
      <div className="res-container">
        {listOfRes.map((restaurants) => (
          <RestaurantCard key={restaurants.info.id} resData={restaurants} />
        ))}
      </div>
    </div>
  );
 }

 export default ResNearYou;

Console:*(Also use breakpoints to view it)*
Component Rendered! // Printed first
Use Effect Callback function Called! // Once the entire component is rendered then this will be printed

It will go line by line but when it encounters useEffect => It'll save the CB function
And move on to next line print Component Rendered! & then the entire component is rendered
Now the useEffect's CB Function will be called.

***Why do we need this?***Because we don't want our user to view an empty component (not a good UX) until the API response is received. ***So we will render the entire component first (with skeleton) and then execute the useEffect's CB Function with API call.**Which will bring the data to us and then we will Re-render our component with populated data.
setListOfRes() will trigger the re-rendering (Whenever STATE VARIABLES are updated React re-renders the Component. Read more about it on smitd.hashnode.dev/react-hooks-reconciliati..)
*

import RestaurantCard from "./RestaurantCard";
import { RES_LIST } from "../utils/mockData";
import { useEffect, useState } from "react";


const ResNearYou =()=>{
  const [listOfRes, setListOfRes] = useState([]);

  useEffect(()=>{
    console.log("Use Effect Callback function Called!");
    fetchData();
  }, [])

  const fetchData = async ()=>{
    // fetch is given us by Browser's JS engine - It will return a Promise
    const data = await fetch("https://www.swiggy.com/dapi/restaurants/......");
    // await for data to come from API


    // await for Promise to get resolved & convert the data to JSON
    const json = await data.json();

    setListOfRes(json?.data?.cards[1]?.card?.card?.gridElements?.infoWithStyle?.restaurants);
    // ! Optional Chaining is important
  }

  return (
    <div className="res-near">
      <h1>Restaurants near you</h1>
      <div className="filter">
        <button
          className="filter-btn"
          onClick={() => {
            const filteredRes = listOfRes.filter(
              (res) => res.info.avgRating > 4
            );
              setListOfRes(filteredRes);
          }}
        >
          Filter Restaurants
        </button>
      </div>
      <div className="res-container">
        {listOfRes.map((restaurants) => (
          <RestaurantCard key={restaurants.info.id} resData={restaurants} />
        ))}
      </div>
    </div>
  );
 }

 export default ResNearYou;

The Swiggy API if used on local may give a CORS error because the API call is made from localhost:1234

Who is blocking us? Our browser block us to call Swiggy's API from localhost from one origin to other.
If there is an origin mis-match the browser blocks that API call.

In order to bypass this CORS Policy Error => We use a CORS Chrome Extension

Now You'll be able to call Swiggy's API from your localhost 🤯
Handle & use Swiggy's API & data to display things on your webpage to meet industry level expectations.

In today's UI world - showing a loader is not a good practise. (spinner, loading...,etc) ‼️
But we have a concept called as Shimmer UI ✅. It means showing a fake page until the data is loaded. (A skeleton, fake cards). This is a much better user experience.
All new webapps uses Shimmer UI (YT, Swiggy, etc)

Loader/Spinner ❌ Shimmer UI ✅
-> Whenever your API is taking some time - load a Shimmer UI quicky
It is psychologically better

Conditional Rendering - rendering something according to a condition

\=> Unless we get the data we show Shimmer Effect once we get the data we display the Cards with data

const ResNearYou =()=>{

return listOfRes.length === 0 ? <Shimmer/> : ( // * Conditional Rendering

<div className="res-near"> ....... ) }

Why do we need STATE Variables?
-> Normal JS variables will not change anything on UI
-> State Variables will keep the Data Layer in-sync with the UI Layer.

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


<li onClick={()=>{
       if(!isSignedIn){
            setIsSignedIn(true);
       } else {
            setIsSignedIn(false);
       }
  }}><FaRegUser/>{ isSignedIn ? 'Sign Out' : 'Sign In'}</li>

React is Keeping track of the STATE VARIABLES.
So whenever a STATE VARIABLE gets updates i.e when (setIsSignedIn()) is called

Behind The Scenes a lot of things happen:
\=> It Updates the isSignedIn Value
\=> React Re-renders the entire component (with the updated value of these state variables).
That means React Reconciliation is triggered: It will find the Diff between the Old V-DOM & New V-DOM. And it'll see only this button is getting updated (the diff). So exactly that will be changed.

React Re-renders the entire component but it is updating only this button's isSignedIn**.** (The Diff between new V-Dom and the old V-Dom).

And React does this super fast. It has the Fastest Render cycle because of This React's Reconciliation Algorithm (React Fiber).

But this is const. How is it getting updated. On re-render this is a new variable.

Search functionality with onChange Handler

import { SWIGGY_URL } from "../utils/constants";
import RestaurantCard from "./RestaurantCard";
import { useEffect, useState } from "react";
import Shimmer from "./Shimmer";


const ResNearYou =()=>{
  const [searchText, setSearchText] = useState("");
  const [filteredRes, setFilteredRes] = useState([]);

  useEffect(()=>{
    console.log("Use Effect Callback function Called!");
    fetchData();
  }, [])

  const fetchData = async ()=>{
    const data = await fetch(SWIGGY_URL);
    const json = await data.json();
    setFilteredRes(json?.data?.cards[1]?.card?.card?.gridElements?.infoWithStyle?.restaurants);
  }
  return listOfRes.length === 0 ? <Shimmer/> : (
    <div className="res-near">
      <h1>Restaurants near you</h1>
      <div className="search-container">
        <input type="text" value={searchText} 
            placeholder="Search for any cuisine or restaurant name" 
            onChange={
               (e)=>{
                setSearchText(e.target.value);
              }
         }/>
        <button onClick={
          ()=>{
            const searchedRes = listOfRes.filter((res) => {
                return res?.info?.name?.toLowerCase()?.includes(searchText?.toLowerCase())
            })
           setFilteredRes(searchedRes);  // Filtered Array
          }
        }>Search</button>
      </div>

      <div className="res-container">
        {filteredRes.map((restaurants) => (
          <RestaurantCard key={restaurants.info.id} resData={restaurants} />
        ))}
      </div>
    </div>
  );
 }

 export default ResNearYou;

One should understand React in-depth you should have an idea why things are behaving in such ways.
This will help you debug things much faster and become a better Developer.

Stay Tuned for new blogs!! 🚀🚀🚀