Getting Started with React

Why React?

JavaScript VS React:

  • Applications can be created using just plain JS, HTML, CSS.
  • But React makes our work easier. One of the biggest and clearest advantage of using React is it's ability to update the UI i.e, Mounting, Re-rendering and Unmounting the UI
  • When you work with JavaScript, you need to write the UI update logic manually, and optimise it further,
  • React abstracts all that work away, through it's implementation of Virtual DOM.
  • So React's biggest help is you don't need to write logic for Updating UI in the most optimised way.

React's Other Features:

  • React Components,
  • React Hooks
  • JSX - a mix of JavaScript and HTML,
  • Routing in React,

What is React?

  • React is a library written using JavaScript. It's used to build UI.
  • There are frameworks, built on top of React, for building the UI:
    NextJS, GatsBy, Remix etc
  • The other competitors for building UI are Frameworks like, Vue.js, Angular, Svelte, EmberJS etc.
  • These frameworks are not built using React.

Note:

  • All the libraries/frameworks for building the UI need to work with JavaScript. Because on UI side only JavaScript works.
  • There other frameworks working with other languages, they're all for Backend/Server side development. Eg: SpringBoot with Java, Django with Python, Flask with Python, RubyOnRails etc.
     
  • UI = Frontend = Client Side
  • Backend = Server side

What is React?

Library:

  • A library is or could be something as simple as just a file having a function written in it.
  • Eg: You can create a function to check if a given number is odd or even, return true or false. You can make this code a library and publish to npm.
  • That's exactly what React is, a library. It only gives you a function, createRoot().
  • You pass your UI element to this function and it renders it on UI.

Frameworks:

  • But frameworks, is something, that defines and controls a lot of things.
  • Like how you create files, how you name them, what to write where to write all those things.
  • Eg: NextJS
  • If you create an app using React, it doesn't dictate your applications folder structure, you can organise your app anyway you want.
  • You can also use React in one part of your app/page.

create-react-app

create-react-app:

  • create-react-app is another library as CLI Tool offered by the React team,
  • it's there if you want to create a new UI app based on React,
  • it gives you a complete project setup all the way to deployment as well (bundling etc),
  • it also offers pre-built templates to create new app with certain technologies
  • **all the pre-generated code is known as Boilerplate code or scaffolding,
  • Official Documentation
In your terminal, execute the following command:

npx create-react-app [app-name]

npx create-react-app my-app --template [tempname]

Eg:
npx create-react-app my-app --template typescript

create-react-app

create-react-app:

  • create-react-app outputs the following boilerplate code files and folders,
  • The most important of these are 3 files:
    - public/index.html,
    - src/index.js,
    - src/App.js, and

    - package.json as well.
  • The code in these 4 files is all that is needed for an application to use React.
     
my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    ├── serviceWorker.js
    └── setupTests.js

create-react-app

// App.jsx
import React from "react";
import './App.scss';

const App = () => {

  return (
    <div className="app">
    	Hello World
    </div>
  );
};
export default App;
// index.js
import React from 'react';
import { createRoot } from 'react-dom/client';

import App from './App';


const rootElement = document.getElementById('root');

const root = createRoot(rootElement);

root.render(<App />);

// index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

create-react-app

// App.jsx
import React from "react";
import './App.scss';

const App = () => {

  return (
    <div className="app">
    	Hello World
    </div>
  );
};
export default App;
// index.js
import React from 'react';
import { createRoot } from 'react-dom/client';

import App from './App';


const rootElement = document.getElementById('root');

const root = createRoot(rootElement);

root.render(<App />);

// index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

create-react-app

create-react-app:

  • App.js: it has the React component which needs to be shown on the UI,
  • index.html: it's the UI page which appears on the browser,
  • this HTML file allows you to pick one HTML element for adding the React component to it
  • index.js: it's the page that adds the the React component (<App/>) to the current UI, for that, it has 2 steps:

    1- it picks the element from HTML file, creates it as root, using the createRoot function of React library
    2- once the root is created, it adds/renders the React component to the root (HTML) element

     
  • ** You can follow the above steps to add React or React based components to any existing app for UI

React Functional Component

React Functional Component (RFC):

  • React Functional components is just a JavaScript function whose name starts with capital letter,
  • RFC returns only one HTML element,
function DivContainer() {
  return (
    <div>Hello There</div>
  );
}

function MyButton() {
  return (
    <button>I'm a button</button>
  );
}

function TwoHTMLs() {
  return (
//     Error
    <div>Hello There</div>
    <button>I'm a button</button>
  );
}

function OneHTML() {
  return (
//   No Error
    <div>Hello There
      <button>I'm a button</button>
    </div>
  );
}

RFC with Logic

React Functional Component (RFC):

  • You can also add some logic in the RFC before the returning the HTML
function DivContainer() {
  const randomNumber = Math.round(Math.random()*100).toFixed(2)
  
  if (randomNumber > 20) {
    console.log("Greater")
  } else {
    console.log("Lesser")
  }
  
  return (
    <div>Hello There</div>
  );
}

JSX

JSX

  • JSX = JavaScript XML
  • We can add JavaScript in the return part of the React Component, along with HTML (JSX)
  • This allows us to add more logic and flexibility to our React components,
  • Conditional Rendering: Render HTML based on some JavaScript logic
// Rendering JS in return along with HTML
// use curly braces {} for adding JS in
// HTML (JSX)
function RandomNumberGenerator() {
  const randomNumber = Math.random()*100)
  
  return (
    <div>Random Number: {randomNumber}</div>
  );
}

// Conditional Rendering
function ConditionalMessage() {
  const randomNumber = Math.random()*100)
  
  return (
    <div>
      {
        randomNumber > 20 
        ? <div>Random Number: {randomNumber}</div> 
        : <div>Sorry, get a number!</div>
      }
    </div>
  );
}

JSX

JSX

  • Although browser doesn't sees the JavaScript written in HTML,
  • If you inspect element, you will see simple HTML, with "class" not "className",
  • That's what the react library does, converts your JSX to simple JS and HTML for browser to understand,
  • The React library sits in browser memory to handle React code
// Conditional Rendering
function RandomNumberGenerator() {
  const randomNumber = Math.random()*100)
  const isRandomNumberGreater = randomNumber > 20
  return (
    <div className="random">
    Random Number Generator
    
    {
      isRandomNumberGreater && 
      <div className="number">
        Random Number: {randomNumber}
      </div>
    }
    
    </div>
  );
}

Intro to State and Hooks

State in React Component:

  • State is nothing but just a variable, but with few special features in a React component,
  • State is used to store and manipulate data in a component,
  • In RFC, to create a state variable, you need to use the useState hook
  • State variable may or may not be empty initially,
import { useState } from "react";

// Conditional Rendering
function RandomNumberGenerator() {
  const [count, setCount] = useState(0)
  
  return (
    <div className="wrapper">
      <button onClick={() => setCount(count + 1)}>
        +
      </button>

      <div>Count: {count}</div>

      <button onClick={() => setCount(count - 1)}>
        -
      </button>
    </div>
  );
}

useState Hook

useState Hook:

  • Hooks are simple JavaScript functions with few rules:
  • There names must start with "use" word,
  • They should be a function,
  • They return something,
  • some hooks are built-in from React
  • You can also create your own hooks, Custom hooks, following the same rules.
import { useState } from "react";

// Conditional Rendering
function RandomNumberGenerator() {
  const [count, setCount] = useState(0)
  
  return (
    <div className="wrapper">
      <button onClick={() => setCount(count + 1)}>
        +
      </button>

      <div>Count: {count}</div>

      <button onClick={() => setCount(count - 1)}>
        -
      </button>
    </div>
  );
}

useState Hook

useState Hook:

  • useState hook is a function that returns an array having exactly 2 values:
    - state variable,
    - state setter function
  • you can name both of them as you want, but conventionally the function is named with "set"
  • state variable represents the value of state,
  • state setter function takes a value and sets it in the state variable,
import { useState } from "react";

// Conditional Rendering
function RandomNumberGenerator() {
  const [count, setCount] = useState(0)
  
  const handlePlusClick = () => {
    const newCount = count + 1;
    setCount(newCount)
  }
  
  const handleMinusClick = () => {
    const newCount = count - 1;
    setCount(newCount)
  }
  
  return (
    <div className="wrapper">
      <button onClick={handlePlusClick}>
        +
      </button>

      <div>Count: {count}</div>

      <button onClick={handleMinusClick}>
        -
      </button>
    </div>
  );
}

useState Hook

useState Hook:

  • functional state update:
    state setter function can also be passed a callback function,
  • this callback function has access to current state value,
  • when you pass a callback to setter function, the return value of the callback is set as state variable's value,
  • if you don't return a value, undefined is set to the state value
import { useState } from "react";

// Conditional Rendering
function RandomNumberGenerator() {
  const [count, setCount] = useState(0)
  
  const handlePlusClick = () => {
    setCount(currentCount => { return currentCount + 1 })
  }
  
  const handleMinusClick = () => {
    setCount(currentCount => { return currentCount - 1 })
  }
  
  return (
    <div className="wrapper">
      <button onClick={handlePlusClick}>
        +
      </button>

      <div>Count: {count}</div>

      <button onClick={handleMinusClick}>
        -
      </button>
    </div>
  );
}

Events In React

Events in React

  • React implements events handling a bit differently,
  • Eg:
    - onClick,
    - onChange,
    - onBlur,
    - onLoad,
    - onKeyUp,
    - onKeyDown,
    - onMouseDown,
    - onMouseUp,
    - onMouseLeave,
    - onCopy,
    - onCut,
    - onPaste,
    - onScroll
import { useState } from "react";

// Conditional Rendering
function RandomNumberGenerator() {
  const [count, setCount] = useState(0)
  
  
  return (
    <div
      onClick={e => console.log('onClick')}
      onMouseEnter={e => console.log('onMouseEnter')}
      onMouseOver={e => console.log('onMouseOver')}
      onMouseDown={e => console.log('onMouseDown')}
      onMouseUp={e => console.log('onMouseUp')}
      onMouseLeave={e => console.log('onMouseLeave')}
    >
      <input
        onChange={e => console.log('onChange')}
        onFocus={e => console.log('onFocus')}
        onBlur={e => console.log('onBlur')}
      />
      </div>
  );
}

Events In React

Events in React

  • React implements events handling a bit differently,
  • Eg:
    - onClick,
    - onChange,
    - onBlur,
    - onLoad,
    - onKeyUp,
    - onKeyDown,
    - onMouseLeave,
    - onCopy,
    - onCut,
    - onPaste
import { useState } from "react";

function InputComponent() {
  const [inputValue, setInputValue] = useState('')
  const [inputFocus, setInputFocus] = useState(false)
  
  const handleInputChange = (e) => {
    setInputValue(e.target.value)
  }
  
  const handleInputFocus = (e) => {
    setInputFocus(true)
  }
  
  return (
    <div>
      <input
        className={`input ${inputFocus && "focus"}`}
        onChange={handleInputChange}
        value={inputValue}
        onFocus={handleInputFocus}
        placeholder={"Enter your name"}
        type="text"
      />
    </div>
  );
}
/* App.css */
.focus {
  outline: orange dashed 2px;
}

Events In React

Events in React

  • Different types of devices emit DOM events differently
  • Synthetic Events: React implements something called as synthetic events.
  • Synthetic events are created to handle events similarly on all the different devices,
import { useState } from "react";

function InputComponent() {
  const [inputValue, setInputValue] = useState('')
  const [inputFocus, setInputFocus] = useState(false)
  
  const handleInputChange = (e) => {
    setInputValue(e.target.value)
  }
  
  return (
    <div>
      <input
        onChange={handleInputValue}
        value={input}
        placeholder={"Enter your name"}
        type="text"
        style={{
          color: inputValue.length > 4 ? "yellow" : "black",
        }}
      />
    </div>
  );
}

Mounting and Re-Rendering

UI Updates in React:

  • When ever the state setter is called, you can see the entire component code is executed again,
  • The complete component UI, i.e, the part written in return, the JSX, is created again,
  • This is called re-rendering of UI.
  • When ever there is a change in the component's state, the entire component code is executed again, and the UI is updated. This is the most powerful aspect of React.
import { useState } from "react";

function InputComponent() {
  const [inputValue, setInputValue] = useState('')
  const [inputFocus, setInputFocus] = useState(false)
  
  const handleInputChange = (e) => {
    setInputValue(e.target.value)
  }
  console.log("1", inputValue)
  
  return (
    <div>
      {console.log("2", inputValue)}
      <input
        onChange={handleInputValue}
        value={input}
        placeholder={"Enter your name"}
        type="text"
        style={{
          color: inputValue.length > 4 ? "yellow" : "black",
        }}
      />
    </div>
  );
}

Mounting and Re-Rendering

Deep Dive React Component Update:

When you open a URL, all the UI elements (HTML elements) are assembled and a DOM Tree is created,

Mounting: Addition of your React component's HTML to the DOM Tree is called Mounting,

When you refresh the page, the existing UI is removed, the entire DOM is dumped, and then again a new one is created,
Unmounting: Removal of your React component's HTML from the DOM Tree is called Unmounting,

Re-rendering: It's a React's special implementation.

import { useState } from "react";

function InputComponent() {
  const [inputValue, setInputValue] = useState('')
  const [inputFocus, setInputFocus] = useState(false)
  
  const handleInputChange = (e) => {
    setInputValue(e.target.value)
  }
  
  return (
    <div>
      <input
        onChange={handleInputValue}
        value={input}
        placeholder={"Enter your name"}
        type="text"
        style={{
          color: inputValue.length > 4 ? "yellow" : "black",
        }}
      />
    </div>
  );
}

Mounting and Re-Rendering

Deep Dive React Component Update:

Re-rendering:

  • The usual way of updating UI was to Mount and UnMount
  • What changed with React is, instead of removing the entire DOM Tree, React just finds the element that needs to be updated, and updates it, keeping the DOM Tree intact,
  • For updating the element, it applies the changes/updates, and re-renders that particular HTML Node,
  • This re-rendering shows the updated UI
  • So now the elements are not unmounted from the DOM Tree, they just keep getting re-rendered on update,
import { useState } from "react";

function InputComponent() {
  const [inputValue, setInputValue] = useState('')
  const [inputFocus, setInputFocus] = useState(false)
  
  const handleInputChange = (e) => {
    setInputValue(e.target.value)
  }
  
  return (
    <div>
      <input
        onChange={handleInputValue}
        value={input}
        placeholder={"Enter your name"}
        type="text"
        style={{
          color: inputValue.length > 4 ? "yellow" : "black",
        }}
      />
    </div>
  );
}

Mounting and Re-Rendering

Deep Dive React Component Update:

Re-rendering:

  • React does this task of Updating /Re-rendering the UI quite meticulously,
  • React maintains a copy of the HTML DOM Tree, called as Virtual DOM,
  • Virtual DOM is DOM created with JavaScript Objects
  • React maintains 2 copies of the Virtual DOM, one represents the current UI, the other represents updated UI,
  • For updating UI, React starts with root element, comparing each element from in both the Virtual DOM copies. This process is called Reconciliation.
import { useState } from "react";

function InputComponent() {
  const [inputValue, setInputValue] = useState('')
  
  const handleInputChange = (e) => {
    setInputValue(e.target.value)
  }
  
  return (
    <div>
      <input
        onChange={handleInputValue}
        value={input}
        placeholder={"Enter your name"}
        type="text"
        style={{
          color: inputValue.length > 4 ? "yellow" : "black",
        }}
      />
    </div>
  );
}

Mounting and Re-Rendering

Deep Dive React Component Update:

Re-rendering:

  • As React finds the differences between current UI and updated UI, it applies the update,
  • Batch Update: React batches all the update changes and applies them all at once
import { useState } from "react";

function InputComponent() {
  const [inputValue, setInputValue] = useState('')
  const [inputFocus, setInputFocus] = useState(false)
  
  const handleInputChange = (e) => {
    setInputValue(e.target.value);
    setInputFocus(true)
  }
  
  return (
    <div>
      <input
        onChange={handleInputValue}
        value={input}
        placeholder={"Enter your name"}
        type="text"
        style={{
          color: inputValue.length > 4 ? "yellow" : "black",
          backgroundColor: inputFocus ? "green" : "white"
        }}
      />
    </div>
  );
}

Mounting and Re-Rendering

Deep Dive React Component Update:

Re-rendering:

  • When you call the state setter function with exact same value, React skips component update and doesn't re-render the component,
  • Also if you call the state setter function from inside a function, you can only get the updated state value, once the function is done execution,
  • If you call multiple state setters, from inside the function, the component updates only once, batch updates.
import { useState } from "react";

function InputComponent() {
  const [inputValue, setInputValue] = useState('')
  const [inputFocus, setInputFocus] = useState(false)
  
  const handleInputChange = (e) => {
    setInputValue(e.target.value);
    setInputFocus(true) // this works only once
    console.log(e.target.value, inputValue, inputFocus)
  }
  
  function handleFocusToggle() {
    setInputFocus(!inputFocus)
  }
  
  return (
    <div>
      <input
        onChange={handleInputValue}
        value={input}
        placeholder={"Enter your name"}
        type="text"
        style={{
          color: inputValue.length > 4 ? "yellow" : "black",
          backgroundColor: inputFocus ? "green" : "white"
        }}
      />
      <button onClick={handleFocusToggle}>Toggle</button>
    </div>
  );
}

State, Local and Global Variables

Variables in a React Component:

  • State Variable:

    - created using useState,
    - its value persists across re-renders,
    - changes to this variable cause re-render

  • Local Variable:
    - normal JS variable created inside React Component,
    - value is lost in component re-render,
    - every re-render creates a new variable,
    - changes to this variable doesn't cause re-render

  • Global Variable:
    - created outside React Component,
    - its value persists across re-renders,
    - changes to this variable don't cause re-render

     

import { useState } from "react";

let globalCount = 100;

function InputComponent() {
  const [inputValue, setInputValue] = useState('')
  const [inputFocus, setInputFocus] = useState(false)
  
  let localVariable = 400;
  
  const handleInputChange = (e) => {
    setInputValue(e.target.value);
  }
  
  function handleFocusToggle() {
    setInputFocus(!inputFocus)
  }
 
  return (
    <div>
      <input
        onChange={handleInputValue}
        value={input}
        style={{
          color: inputValue.length > 4 ? "yellow" : "black",
        }}
      />
      <button onClick={handleFocusToggle}>Toggle</button>
    </div>
  );
}

useRef Hook

useRef Hook:

  • it's a hook which replaces the document.getElementById and other DOM methods,
  • this hook let's you access the HTML element, and then modify it directly,
  • if you add the ref variable to an HTML element, you can access the element in the current object of ref variable (ref.current)
import { useState, useRef } from "react";

function InputComponent() {
//   const [inputValue, setInputValue] = useState('')

  const inputRef = useRef('')
  const handleInputChange = (e) => {
    inputRef.current.value = e.target.value);
  }
  
 
  return (
    <div>
      <input
        ref={inputRef}
        onChange={handleInputValue}
// 		value={input}
        style={{
          color: inputValue.length > 4 ? "yellow" : "black",
        }}
      />
    </div>
  );
}

useRef Hook

useRef Hook:

  • the useRef hook creates a variable (ref variable),
  • the value in this variable persist across re-renders,
  • changes to this variable do not cause re-render of component,
  • the updates can be accessed instantly, unlike the state variable,
import { useState, useRef } from "react";

function InputComponent() {
  const [toggle, setToggle] = useState(false)
  const inputRef = useRef('')
  
  const handleInputChange = (e) => {
    inputRef.current.value = e.target.value);
    console.log(inputRef.current.value)
  }
  
  console.log(inputRef.current.value)
  
  return (
    <div>
      <input
        ref={inputRef}
        onChange={(e) => {
          handleInputValue(e);
          setToggle(!toggle)
        }}
//         value={input}
        style={{
          color: inputValue.length > 4 ? "yellow" : "black",
        }}
      />
    </div>
  );
}

Nesting React Components

Nested React Components:

  • A React component is used just like an HTML element,
  • You can add one or more React Components (child) in the "return" statement of another React component (parent),
  • If the parent component, re-renders, the child component will also re-render.
import { useState, useRef } from "react";

function Parent() {
  const [toggle, setToggle] = useState(false)
  
  return (
    <div>
      <ChildComponent/>
    </div>
  );
}

const ChildComponent = () => {
  return (
    <div>
       I'm a Child Comp
    </div>
  )
}

Props in React

Passing Data from Parent To Child React Comp:

  • We can pass data from Parent Comp to Child Comp
  • This data is called as prop (short for properties),
  • props are IMMUTABLE objects,
  • all props are passed in one single object to the child,
  • can pass anything as prop, function, value, object etc.
  • Change in props, also trigger a re-render in the component receiving those props (child),
import { useState, useRef } from "react";

function Parent() {
  const [toggle, setToggle] = useState(false)
  const address = {
    line1: "245, C sector",
    line2: "Indrapuri",
    city: "Bhopal"
  }
  
  return (
    <div>
      <ChildComponent name={"Yash"} age={30} add={address}/>
    </div>
  );
}

const ChildComponent = (propObject) => {
  const { name, add } = propObject;
  return (
    <div>
      <div>{name}</div>
      <div>{propObject.age}</div>
    </div>
  )
}

Props Drilling in React

Passing Props Deep Down:

  • Multi-level nesting:
    - React components can be nested upto multiple levels,
  • Passing props to 4th or 5th child component:
    - If you have to pass some data from 1st component to 5th or 6th component, you will have to pass the props down every component in the line, just to reach the 5th component,
  • this is called props drilling: passing props to a component which doesn't use it, just passes it to next component
import { useState, useRef } from "react";

function Parent() {
  const [value, setValue] = useState(100)
  return (
    <div>
      <Comp1 prop1={value}/>
    </div>
  );
}

const Comp1 = (propObject) => {
  return (
    <div>
      <Comp3 prop3={propObject.prop1}/>
    </div>
  )
}

const Comp3 = (propObject) => {
  return (
    <div>
      {propObject.prop3}
    </div>
  )
}

useContext in React

Passing Props Deep Down:

  • prop drilling is an anti-pattern,
  • to resolve this we have useContext hook and createContext function,
  • First we create a Context by calling the createContext function,
  • It returns a Context component, which has a Provider for passing the value (prop)
  • Then we use the useContext hook to access the value, directly in the child component,
  • All the components in the line can use the prop through useContext,
import { useState, useContext, createContext } from "react";
// Step 1 - Create Context
const PropContext = createContext();

function Parent() {
  const [value, setValue] = useState(100)
  
  // Step 2 - Wrap ypur component in provider
  return (
      <PropContext.Provider value={{value}}>
        <Comp1 />
      </PropContext.Provider>
  );
}

const Comp1 = () => {
  return (
    <div>
      <Comp3 />
    </div>
  )
}

const Comp3 = () => {
  // Step 3 - Extract data through useContext
  const { value } = useContext(PropContext)
  return (
    <div>
      {value}
    </div>
  )
}

Re-render with props in React

With multi-level nested Components:

  • If a Component is receiving some props,
  • and if the value of the props have changed,
  • then the component also re-renders,
  • if the prop is being passed down through multiple components, the entire set of components (tree) would re-render,
  • when you pass props down the line using context, then also all the components in the tree would re-render,
import { useState, useContext, createContext } from "react";
// Step 1 - Create Context
const PropContext = createContext();

function Parent() {
  const [value, setValue] = useState(100)
  
  // Step 2 - Wrap ypur component in provider
  return (
      <PropContext.Provider value={{value}}>
        <Comp1 />
      </PropContext.Provider>
  );
}

const Comp1 = () => {
  return (
    <div>
      <Comp3 />
    </div>
  )
}

const Comp3 = () => {
  // Step 3 - Extract data through useContext
  const { value } = useContext(PropContext)
  return (
    <div>
      {value}
    </div>
  )
}

Re-renders in React

A React Component re-renders due to exactly 3 reasons:

  • State: If the component has some state in it. The state is updated using the setState with a new value, then the component re-renders
  • Props: If a Component is receiving some props, and if the value of the props have changed, then the component also re-renders,
  • Parent: If the Parent component re-renders, then all of its children component and their children re-render
import { useState, useContext, createContext } from "react";
// Step 1 - Create Context
const PropContext = createContext();

function Parent() {
  const [value, setValue] = useState(100)
  
  // Step 2 - Wrap ypur component in provider
  return (
      <PropContext.Provider value={{value}}>
        <Comp1 />
      </PropContext.Provider>
  );
}

const Comp1 = () => {
  return (
    <div>
      <Comp3 />
    </div>
  )
}

const Comp3 = () => {
  // Step 3 - Extract data through useContext
  const { value } = useContext(PropContext)
  return (
    <div>
      {value}
    </div>
  )
}

useEffect Hook in React

useEffect Hook:

  • It receives 2 args:
    - a callback function,
    - an array (optional),
  • useEffect runs the callback function every time after the component renders (mounting, or re-render),
  • when the component renders for the first time, useEffect is called, after the component has rendered,
  • Again when the component re-renders, the useEffect can be controlled to run or not
import { useState, useEffect } from "react";

function App() {
  const [value, setValue] = useState(100)
  
  useEffect(() => {
    console.log('effect called')
    // this effect runs only on first 
    // render of the component (mounting)
  }, [])
  
  return (
    <div className="app">
      Show value: {value}
    <div/>
  );
}

export default App;

useEffect Hook in React

3 cases of useEffect Hook:

  • No 2nd arg: useEffect without 2nd arg: runs on mounting and then every render (re-render) of component
     
  • Empty array: useEffect with 2nd arg empty array: runs only on first render (mount), and not again on re-renders
     
  • Value in dependency array: useEffect with a state value in dependency array, runs on mount and then only when the state value in the dependency array changes
import { useState, useEffect } from "react";

function App() {
  const [value, setValue] = useState(100)
  
  useEffect(() => {
    console.log('effect called on every render')
    // this effect runs on mount and then every 
    // re-render of the component
  })
  
  useEffect(() => {
    console.log('effect called on mount only')
    // this effect runs only on first 
    // render of the component (mounting)
  }, [])
  
  useEffect(() => {
    console.log('effect called on every render')
    // this effect runs "value" variable
    // changes on call "setValue"
  }, [value])
  
  return (
    <div className="app">
      Show value: {value}
      <input 
        onChange={(e) => setValue(e.target.value)} 
        value={value}
      />
    <div/>
  );
}

export default App;

useEffect Hook in React

import { useState, useEffect } from "react";

let globalCount = 20;
function App() {
  const [value, setValue] = useState(100)
  let localCount = 50;
  const newRef = useRef(0)
  
  useEffect(() => {
    console.log('effect runs on mount')
    // then it never runs, because
    // changes to localCount or globalCount or newRef 
    // variable are not tracked
  }, [ localCount, globalCount, newRef.current ])
  
  useEffect(() => {
    console.log('effect runs on mount')
    // then it runs only when 
    // "value" state changes
  }, [value])
  
  return (
    <div className="app">
      Show value: {value}
      <input 
        onChange={(e) => setValue(e.target.value)} 
        value={value}
      />
    <div/>
  );
}

export default App;

useEffect Hook:

  • useEffect with 2nd arg non-empty array: runs only on the first render (mount),
  • and then after it runs only when any value in the dependency array changes,
  • if the value in dependency array is either a state, or ref variable then only it triggers the effect in it.
  • The effect doesn't run if any local or global variables present in dependency array change

useEffect Hook in React

import { useState, useEffect } from "react";

let globalCount = 20;
function App() {
  const [value, setValue] = useState(100);
  
  useEffect(() => {
    setValue(value + 1)    
  }, [value])
  
  useEffect(() => {
    setValue(value + 1)    
  })
  
  return (
    <div className="app">
      Show value: {value}
      <input 
        onChange={(e) => setValue(e.target.value)} 
        value={value}
      />
    <div/>
  );
}

export default App;

useEffect with 2nd arg non-empty array:

  • runs on the first render (mount),
  • and then after it runs only when any value in the dependency array changes,
  • if dependency array has a state variable in it, eg: "value"
  • And you call the state setter ("setValue") inside the hook, then it goes for infinite re-render
  • Same if there's 2nd arg at all.

useEffect Hook in React

useEffect with return:

  • useEffect's first argument is a callback function,
  • functions can have return,
  • useEffect's callback function returns only a function
  • this function is called right before a component re-renders
  • This useEffect runs every time "value" state changes,
  • So the "return" on line 12 runs before the component re-renders
  • This is used as cleanup function to removeEventListeners etc
import { useState, useEffect } from "react";

let globalCount = 20;
function App() {
  const [value, setValue] = useState(100)
  
  useEffect(() => {
    console.log('effect runs on mount')
    // then it runs only when 
    // "value" state changes

    return () => { console.log("Hi") }
  }, [value])
  
  return (
    <div className="app">
      Show value: {value}
      <input 
        onChange={(e) => setValue(e.target.value)} 
        value={value}
      />
    <div/>
  );
}

export default App;

useEffect Hook in React

useEffect with return:

  • the example here won't cause an infinite re-render because setValue is being called from inside the return
  • when the component has to re-render,
  • it goes for unmount first,
  • return is called on unmount,
  • then after unmount the component renders,
  • the setState change is applied in the component render,
  • hence no new re-render takes place
import { useState, useEffect } from "react";

let globalCount = 20;
function App() {
  const [value, setValue] = useState(100)
  
  useEffect(() => {
//     this won't cause an infinite re-render
//     because the setValue is called when 
//     the component is being unmount before
//     a re-render
    return () => { setValue(value + 1) }
  }, [value])
  
  return (
    <div className="app">
      Show value: {value}
      <input 
        onChange={(e) => setValue(e.target.value)} 
        value={value}
      />
    <div/>
  );
}

export default App;

useEffect Hook in React

calling APIs in useEffect:

  • Since the API calls take time, and UI should not wait for API call to complete,
  • rather it should show something when the api call is going on, like a loader or shimmer,
  • API calls are mostly put inside the useEffect,
  • because useEffect runs after the component has rendered on the UI
  • so now the user has something on the UI while the API is called on the background, as a side effect from the component,
  • Never make the useEffect callback function as async because the useEffect hook should return either nothing (undefined) or a cleanup function. If you make the callback function async, it returns a promise, which is not the intended use case for useEffect.
import { useState, useEffect } from "react";

function App() {
  const [userData, setUserData] = useState([])
  const API = "https://jsonplaceholder.typicode.com/users"
  
  useEffect(() => {
    async function callUserAPI() {
      const getUserData = await fetch(API)
      const userDataJSON = await getUserData.json()
      console.log(userDataJSON)
      setUserData(userDataJSON)
    }
    
    callUserAPI()
  }, [])
  
  return (
    <div className="app">
      {userData.length === 0 
        ? <Loader/> 
        : userData.map(user => <User user={user}/>)
      }
    <div/>
  );
}

export default App;

Routing in React

Routing in React

  • https://github.com/Sachinrawat1215/React-Router-DOM-version6/blob/master/src/index.js
import { useState, useEffect } from "react";

function App() {
  const [userData, setUserData] = useState([])
  const API = "https://jsonplaceholder.typicode.com/users"
  
  useEffect(() => {
    async function callUserAPI() {
      const getUserData = await fetch(API)
      const userDataJSON = await getUserData.json()
      console.log(userDataJSON)
      setUserData(userDataJSON)
    }
    
    callUserAPI()
  }, [])
  
  return (
    <div className="app">
      {userData.length === 0 
        ? <Loader/> 
        : userData.map(user => <User user={user}/>)
      }
    <div/>
  );
}

export default App;