React Basic Learning

 

  • React is js libary for building dynamic and interactive user interface.
  • It was created by facebook in 2011 and widely used frontend development.

Who to install react

  • we need node version 16 or higer
  • download latest version of node.

How to create a React App

2 way to create react app

official way provided by react team

→ create react app

→ vite → another tool, it is very popular because it is faster and give small bundle size.

Process

  • npm create vite@latest - to install latest version of vite
  • there will be guide how to setup vite
  • after npm install
  • cd project name
  • and do npm run dev

Project Structure

image.png

  • we can see node_modules where all node package are kept when we install other third package these are all stored here.
  • It is good practice to put these modules in .gitignore file because it might be very heavy file.
  • public file where all images that we use can be stored here.
  • .gitignore to ignore file and folder that we don’t want to store in git
  • index. html plain html file to create webpage template
  • package-lock.json it is temporary file to support package.json file
  • package.json it is file to see what project use for development like other 3rd package libary and script and many more
  • tsconfig.json we never touch this file unless we know what we trying to do this is typescript configuration file.
  • same for vite.config.ts

To create react component - create new file message.tsx

2 ways to create react component

  • class component → old way to create component
  • functional component → new ,popular and easy way to create compoent.
import React from "react";

const Message = () => {
  return <h1>Hello World.</h1>;
};

export default Message;
// it is simple and self expanation code 
const Message = () => {
  const name = "bishal";
  if (name) return <h1>Hello {name}</h1>;
  return <h1>Hello World.</h1>;
};

export default Message;

Install extension called ES7+ in visual studio code to use

rafce → shortcut for react arrow function export create a function component.

Conditional Rendering

Sometime we want to render content based on condition like.

if items.length === 0 render nothing to display.

how to do that ?

import React from "react";

const ListGroup = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  //   cities.length = 0;
  if (cities.length === 0) return <p>No Items to display.</p>;
  return (
    <div>
    
      {cities.map((city, index) => (
        <p key={index}>{city}</p>
      ))}
    </div>
  );
};

export default ListGroup;

if we want heading like

import React from "react";

const ListGroup = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  //   cities.length = 0;
  if (cities.length === 0)
    return (
      <>
        <h1>Cities</h1>
        <p>No Items to display.</p>
      </>
    );
  return (
    <div>
      <h1>Cities</h1>
      {cities.map((city, index) => (
        <p key={index}>{city}</p>
      ))}
    </div>
  );
};

export default ListGroup;

  • But we duplicate same heading twice it’s bad practice

instead we can use ternary operator

import React from "react";

const ListGroup = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  //   cities.length = 0;
  return (
    <div>
      <h1>Cities</h1>
      {cities.length === 0 ? <p>No items to display.</p> : null}
      <ul>
        {cities.map((city, index) => (
          <li key={index}>{city}</li>
        ))}
      </ul>
    </div>
  );
};

export default ListGroup;

sometimes our logic get complicated and pollute JSX markup.

In those case we can store them in separate const or variable like

import React from "react";

const ListGroup = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  //   cities.length = 0;
  const message = cities.length === 0 ? <p>No items to display.</p> : null;
  return (
    <div>
      <h1>Cities</h1>
      {message}
      <ul>
        {cities.map((city, index) => (
          <li key={index}>{city}</li>
        ))}
      </ul>
    </div>
  );
};

export default ListGroup;

we can also move logic inside function

Benefit of using it is we can pass argument.

import React from "react";

const ListGroup = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  //   cities.length = 0;

  function getMessage() {
    return cities.length === 0 ? <p>No items to display.</p> : null;
  }
  return (
    <div>
      <h1>Cities</h1>
      {getMessage()}
      <ul>
        {cities.map((city, index) => (
          <li key={index}>{city}</li>
        ))}
      </ul>
    </div>
  );
};

export default ListGroup;

Even more concise way to write same code

import React from "react";

const ListGroup = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  //   cities.length = 0;

  return (
    <div>
      <h1>Cities</h1>
      {cities.length === 0 && <p>No items to display.</p>}
      <ul>
        {cities.map((city, index) => (
          <li key={index}>{city}</li>
        ))}
      </ul>
    </div>
  );
};

export default ListGroup;

Handling Events

We will see how to handle click even in component

  • In react each item have property or prop onClick
import React from "react";

const ListGroup = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  //   cities.length = 0;

  return (
    <div>
      <h1>Cities</h1>
      {cities.length === 0 && <p>No items to display.</p>}
      <ul>
        {cities.map((city, index) => (
          <li key={index} onClick={() => console.log("clicked", city, index)}>
            {city}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ListGroup;

arrow function inside onClick can optionally have ‘browser event’. We can call it ‘e’ or ‘event’

{cities.map((city, index) => (
          <li key={index} onClick={(e) => console.log(e)}>
            {city}
          </li>
        ))}

If we do this, we get object and its type is SyntheticBaseEven.

  • It is built in class in react beacuse different browser have different implementation of event object.
  • To make cross browser react team created class ‘SyntheticBaseEvent’.
  • This is wrapper around native event object.

image.png

It is 1 line, but if this function is complex and then we need to move function to other and reference that function.

  • If we hover over the e we can see it’s type and declare it’s type in referenced function.

like

import { MouseEvent } from "react";
const ListGroup = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  //   cities.length = 0;
  const handleClick = (e: MouseEvent) => {
    console.log(e);
  };
  return (
    <div>
      <h1>Cities</h1>
      {cities.length === 0 && <p>No items to display.</p>}
      <ul>
        {cities.map((city, index) => (
          <li key={index} onClick={handleClick}>
            {city}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ListGroup;

Managing States

Now, when we click on list item we want them to highlight it.

  • To that we have active class in bootstrap.
  • we need variable that keep track of selected item,
import { MouseEvent } from "react";
const ListGroup = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  //   cities.length = 0;
  let selectedIndex = -1;
  const handleClick = (index: number, e: MouseEvent) => {
    selectedIndex = index;
    console.log(selectedIndex);
  };
  return (
    <div>
      <h1>Cities</h1>
      {cities.length === 0 && <p>No items to display.</p>}
      <ul className="list-group">
        {cities.map((city, index) => (
          <li
            className={
              selectedIndex === index
                ? "list-group-item active"
                : "list-group-item"
            }
            key={index}
            onClick={(e) => handleClick(index, e)}
          >
            {city}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ListGroup;

If we click item, we don’t see changes in browser window but console we see value. Why that?

image.png

  • React doesn’t re-render when normal variable change.
  • The variable we declare selectedIndex is local to functional component.
  • To solve, we need to tell react it have data or state that might change overtime.
  • To implement that we have built in function called useState.
  • useState is hook that allows to tap into built-in features in react.
  • using hook we can tell react that data or state may change over time.
  • useState return array with 2 value.
  • 1st value is variable itself. and 2nd is updater function to update variable.
  • we can destructor array to work easily.

We use useState hook like this

import { MouseEvent, useState } from "react";
const ListGroup = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  //   cities.length = 0;
  const [selectedIndex, setSelectedIndex] = useState(-1);
  const handleClick = (index: number, e: MouseEvent) => {
    setSelectedIndex(index);
  };
  return (
    <div>
      <h1>Cities</h1>
      {cities.length === 0 && <p>No items to display.</p>}
      <ul className="list-group">
        {cities.map((city, index) => (
          <li
            className={
              selectedIndex === index
                ? "list-group-item active"
                : "list-group-item"
            }
            key={index}
            onClick={(e) => handleClick(index, e)}
          >
            {city}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ListGroup;

Now, we see changes in browser.

and each component is going to have own state.

example if we have

<div>
      <ListGroup />
      <ListGroup />
    </div>

1 state don’t affect other state.

Passing Data via Props

  • we are showing list of cities in ListGroup component.
  • But what if we want to show list of color or name.
  • we don’t want to create separate component for that.

How can we make this component reusable?

→ This is where we use props or properties

  • Props are input to our components.
  • Instead of define items element we should pass them as a props or input.
  • To do that, we use typescript feature called interface.

like

import { MouseEvent, useState } from "react";
interface Props {
  cities: string[];
  heading: string;
}
const ListGroup = ({ cities, heading }: Props) => {
  //   cities.length = 0;
  const [selectedIndex, setSelectedIndex] = useState(-1);
  const handleClick = (index: number, e: MouseEvent) => {
    setSelectedIndex(index);
  };
  return (
    <div>
      <h1>{heading}</h1>
      {cities.length === 0 && <p>No items to display.</p>}
      <ul className="list-group">
        {cities.map((city, index) => (
          <li
            className={
              selectedIndex === index
                ? "list-group-item active"
                : "list-group-item"
            }
            key={index}
            onClick={(e) => handleClick(index, e)}
          >
            {city}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ListGroup;

And pass data from App

import React from "react";
import ListGroup from "./ListGroup";

const App = () => {
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  return (
    <div>
      <ListGroup cities={cities} heading="Cities of Nepal" />
    </div>
  );
};

export default App;

Passing Function via Props

In real world, something should happen after an item is selected.

  • May be we want to redirect the user to other page, place order if buy button is clicked in ecommerce.
  • We need a mechanism to notify a consumer or parent component, that item is selected.

App is parent of ListGroup in this case.

When item is selected we should notify App component.

How to implement?

→ Currently we have 2 items in props, we can add 3rd propery function.

  • when we click it we call function with this and can notify parent.
import { MouseEvent, useState } from "react";
interface Props {
  cities: string[];
  heading: string;
  onSelect: (city: string) => void;
}
const ListGroup = ({ cities, heading, onSelect }: Props) => {
  //   cities.length = 0;
  const [selectedIndex, setSelectedIndex] = useState(-1);

  return (
    <div>
      <h1>{heading}</h1>
      {cities.length === 0 && <p>No items to display.</p>}
      <ul className="list-group">
        {cities.map((city, index) => (
          <li
            className={
              selectedIndex === index
                ? "list-group-item active"
                : "list-group-item"
            }
            key={index}
            onClick={() => {
              setSelectedIndex(index);
              onSelect(city);
            }}
          >
            {city}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ListGroup;

import React from "react";
import ListGroup from "./ListGroup";

const App = () => {
  const handleSelect = (item: string) => {
    console.log(item);
  };
  let cities = ["kathmandu", "bhaktapur", "Kavre"];
  return (
    <div>
      <ListGroup
        cities={cities}
        heading="Cities of Nepal"
        onSelect={handleSelect}
      />
    </div>
  );
};

export default App;

function to call onClick

State vs Props

Props

  • Input passed to a component
  • Similar to function args
  • we should treat them as immutable→ unchangeable (read only)
  • example :- heading=’ ’ → no error will show but not a good practice.

State

  • Data managed by a component
  • Similar to local variables
  • Mutable → changeable
  • Every time state change react will re-render Dom accordingly.

Passing Children

Some times we need to pass children.

import React from "react";
interface Props {
  children: string;
}
const Alert = ({ children }: Props) => {
  return <div>{children}</div>;
};

export default Alert;

import React from "react";
import Alert from "./Alert";

const App = () => {
  return (
    <div>
      <Alert>Hello world</Alert>
    </div>
  );
};

export default App;

This is simple example where we pass string as children, but what if we need to pass complex jsx or tsx markup.

then we need to use ReactNode like

import React from "react";
import Alert from "./Alert";

const App = () => {
  return (
    <div>
      <Alert>
        <p>
          Hello <span>world</span>
        </p>
      </Alert>
    </div>
  );
};

export default App;

import { ReactNode } from "react";
interface Props {
  children: ReactNode;
}
const Alert = ({ children }: Props) => {
  return <div>{children}</div>;
};

export default Alert;

Download react dev tools extension for chrome

to see component and profiler tab

Exercise : Building a Button Component

import React from "react";
import Alert from "./Alert";
import Button from "./component/Button";

const App = () => {
  return (
    <div>
      <Button color="primary">Click Me</Button>
    </div>
  );
};

export default App;

interface Props {
  children: string;
  color: string;
}
const Button = ({ children, color }: Props) => {
  return <button className={"btn btn-" + color}>{children}</button>;
};

export default Button;

But this this we can pass any color, invalid color also,

to solve we do

interface Props {
  children: string;
  color?: "primary" | "secondary" | "danger";
}
const Button = ({ children, color }: Props) => {
  return <button className={"btn btn-" + color}>{children}</button>;
};

export default Button;

with this we can supply only the specified color in above interface

and ? is optional chaining operator means it is optional props.

we don’t get error if we don’t pass color.

Exercise : Showing an Alert

When click on button we see alter and we have close button when we click it, it should disappear.

import React, { useState } from "react";
import Alert from "./Alert";
import Button from "./component/Button";

const App = () => {
  const [visisble, setVisisble] = useState(false);
  const handleClick = () => {
    setVisisble(true);
  };
  return (
    <div>
      {visisble && (
        <Alert onClose={() => setVisisble(false)}>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia, eos.
        </Alert>
      )}
      <Button onClick={handleClick} color="primary">
        Alert
      </Button>
    </div>
  );
};

export default App;

import { ReactNode } from "react";
interface Props {
  children: ReactNode;
  onClose: () => void;
}
const Alert = ({ children, onClose }: Props) => {
  return (
    <div
      className="alert alert-warning alert-dismissible fade show"
      role="alert"
    >
      {children}
      <button
        onClick={onClose}
        type="button"
        className="btn-close"
        data-bs-dismiss="alert"
        aria-label="Close"
      ></button>
    </div>
  );
};

export default Alert;

interface Props {
  children: string;
  color?: "primary" | "secondary" | "danger";
  onClick: () => void;
}
const Button = ({ children, color, onClick }: Props) => {
  return (
    <button className={"btn btn-" + color} onClick={onClick}>
      {children}
    </button>
  );
};

export default Button;

Styling

Using vanilla css

  • create component and add ListGroup.css
  • remove bootstrap import in main.tsx
  • cohesion- things that are related should be next to each other. so ListGroup.css and ListGroup.tsx are highly related to each other so we don’t put those in separate file.

In ListGroup.css

.listGroup{
list-style: none;
padding:0
}
import React, { useState } from "react";
import "./ListGroup.css";
import { MdPermContactCalendar } from "react-icons/md";

interface Props {
  item: string[];
  heading: string;
  onSelectItem: (item: string) => void;
}
const ListGroup = ({ item, heading, onSelectItem }: Props) => {
  const [select, setSelect] = useState(-1);
  const getMessage = () => (item.length === 0 ? <p>No item found.</p> : null);
  //   if (item.length === 0) return <p>No item found.</p>;
  const header = `${item} hello world`;
  return (
    <>
      <h2>{heading}</h2>
      {getMessage}
      <ul className={[styles.listGroup, styles.container].join(" ")}>
        {item.map((item, key) => (
          <li
            className={
              select === key ? "active list-group-item" : "list-group-item"
            }
            onClick={() => {
              setSelect(key);
              onSelectItem(item);
            }}
            key={key}
          >
            {item}
          </li>
        ))}
        <p style={{ backgroundColor: "red" }}>hello world</p>
      </ul>
      <MdPermContactCalendar color="green" size={30} />
    </>
  );
};

export default ListGroup;

CSS Modules

  • There is a problem with vanilla css
  • if somewhere else if we have stylesheet with same className we run into clashes.

Example:- in ListGroup.css

.listGroup{
list-style: none;
padding:0
}

In App.css

.listGroup{
background:red;
}

In App.tsx

import ‘./App.css’

  • Back to browser we ran into clashes.

To solve this problem , we use css module

  • A css module is a css file in which all className are scoped locally. Just like JS module.
  • To do this - 1st we rename file add module like
  • ListGroup.module.css
  • and in ListGroup.tsx change import like

import styles from "./ListGroup.module.css";

every css class that we define here will be property on that object of styles

In ListGroup.tsx

  • this is example to use multiple styles

<ul className={[styles.listGroup, styles.container].join(" ")}>

now we see difference because modules append extra separate value in class that prevent name clash.

import React, { useState } from "react";
import styles from "./ListGroup.module.css";
import { MdPermContactCalendar } from "react-icons/md";

interface Props {
  item: string[];
  heading: string;
  onSelectItem: (item: string) => void;
}
const ListGroup = ({ item, heading, onSelectItem }: Props) => {
  //   item.length = 0;
  const [select, setSelect] = useState(-1);
  const getMessage = () => (item.length === 0 ? <p>No item found.</p> : null);
  //   if (item.length === 0) return <p>No item found.</p>;
  const header = `${item} hello world`;
  return (
    <>
      <h2>{heading}</h2>
      {getMessage}
      <ul className={[styles.listGroup, styles.container].join(" ")}>
        {item.map((item, key) => (
          <li
            className={
              select === key ? "active list-group-item" : "list-group-item"
            }
            onClick={() => {
              setSelect(key);
              onSelectItem(item);
            }}
            key={key}
          >
            {item}
          </li>
        ))}
        <p style={{ backgroundColor: "red" }}>hello world</p>
      </ul>
      <MdPermContactCalendar color="green" size={30} />
    </>
  );
};

export default ListGroup;

CSS in JS

  • Another approch to style component is CSS-in-JS.
  • Some people love it and some hate it. It is controversal technology.
  • Idea is we can write all the styles for a component next to its definition in JS or TS

It gives us number of benefit.

  • Scoped Styles → no name confilct.
  • all the css and JS/TS code in one place → that means if tommoro we don’t need component we can delete it in single place. That is not going to impact anywhere in application.
  • Easier to delete a component.
  • Easier to style based on props/State

Different libraries implement this concept

  • styled component → we look at styled component
  • Emotion
  • Polished

Remove css module create in previous lecture and install styled-component

like

npm i styled-components

can also styled using props

import React, { useState } from "react";
import { styled } from "styled-components";
interface Props {
  item: string[];
  heading: string;
  onSelectItem: (item: string) => void;
}
interface ListItemProps {
  active: boolean;
}
const List = styled.ul`
  list-style: none;
  padding: 0;
`;
const ListItem = styled.li<ListItemProps>`
  padding: 5px 0;
  background: ${(props) => (props.active ? "blue" : "transparent")};
  color: ${(props) => (props.active ? "white" : "black")};
`;

const ListGroup = ({ item, heading, onSelectItem }: Props) => {
  const [select, setSelect] = useState(-1);
  return (
    <>
      <h2>{heading}</h2>

      <List>
        {item.map((item, key) => (
          <ListItem
            active={select === key}
            onClick={() => {
              setSelect(key);
              onSelectItem(item);
            }}
            key={key}
          >
            {item}
          </ListItem>
        ))}
      </List>
    </>
  );
};

export default ListGroup;

separation of concerns

This principle recommends that ‘Divide a program into distinct sections where each section handles a specific functionality, rather than having everything in 1 place.’

With this our program will be

  • modular
  • easier to understand
  • easier to maintain
  • easier to modify

If our program is modular we can build and test module independently & reuse them in other program

In modular program, each module is responsible for single concern. Similar people working in restaurant have distinct roles.

  • eg chef only cooks, server only server
  • In modules all complexity and implementation detail are hidden behind well define interface.
  • Think of remote control of tv, complex of remote are hidden behind and give button to use.
  • Some people argue that css-in-js violet separation concern principle because we put every thing in same file.
  • But that not main idea of separation of concern
  • this principal doesn’t ditect where we should put every thing in 1 file or seperate in multiple file.
  • Instead it empsize that different sections or module in program should handle specific functionality and all complexity around a piece of functionality should be hidden behind a well define interface.

Inline Styles

  • we can write inline style similarly how we use in html and css.
  • It is not recommend because it makes code hard to read and maintain.
  • we should save them for every special cases.
interface Props {
  children: string;
  color?: "primary" | "secondary" | "danger";
  onClick: () => void;
}
const Button = ({ children, color, onClick }: Props) => {
  return (
    <button
      style={{ color: "gold", padding: "3px 1px" }}
      className={"btn btn-" + color}
      onClick={onClick}
    >
      {children}
    </button>
  );
};

export default Button;

We can also do like this separate object to define style.

interface Props {
  children: string;
  color?: "primary" | "secondary" | "danger";
  onClick: () => void;
}
const Button = ({ children, color, onClick }: Props) => {
  const styles = { color: "gold", padding: "3px 1px" };
  return (
    <button style={styles} className={"btn btn-" + color} onClick={onClick}>
      {children}
    </button>
  );
};

export default Button;

Adding Icons

There are may popular library to add icons like react-icons, lucid-icons and many more.

we will talk about react-icons

npm i react-icons

to install react-icons libary

and can use like

import { FaGlobeAfrica } from "react-icons/fa";
interface Props {
  children: string;
  color?: "primary" | "secondary" | "danger";
  onClick: () => void;
}
const Button = ({ children, color, onClick }: Props) => {
  const styles = { color: "gold", padding: "3px 1px" };
  return (
    <>
      <button style={styles} className={"btn btn-" + color} onClick={onClick}>
        {children}
      </button>
      <FaGlobeAfrica color="blue" size="40" />
    </>
  );
};

export default Button;

Managing Component State

State management is a funcdamental concept in react.

we will understand it deeply

Understanding State Hook

  • using state hook we can add state to component

Few things to know about state hook for us.

1st

  • React updates state asynchronously → meaning not immediately

Example

import React, { useState } from "react";

const state = () => {
  const [visisble, setVisisble] = useState(false);
  const handleClick = () => {
    setVisisble(true);
    console.log(visisble);
  };
  return (
    <div>
      <button onClick={handleClick}>Show</button>
    </div>
  );
};

export default state;

Now when I click on Show button.

  • we see old value ‘false’.
  • Because react update state asynchronously means not immediately.
  • This is done for performance reasons. Because as part of handling an even, we could set multiple state variable
  • Every time we call set function ,react re-render our component and end up too many re-renders which is unnecessary. So for performance reason react takes all of these updates, batches them and applies them at a later time.
  • And when handler finish execution, react applies all the updates at once and then it will re-render out component with the updated state.

2nd

  • State is stored outside of component.

let see in example

import React, { useState } from "react";

const state = () => {
  const [visisble, setVisisble] = useState(false);
  let count = 0;
  const handleClick = () => {
    setVisisble(true);
    count++;
    console.log(visisble);
  };
  return (
    <div>
      <button onClick={handleClick}>Show</button>
    </div>
  );
};

export default state;
  • variable declare inside function are scoped to that function means local to that function.
  • so when function finish execution, our local variable is going to be removed from the memory.
  • That means the next time react re-renders this component it going to call app component function again, so count variable will be 0 again.
  • So update applied in count++ will be lost.
  • This is reason we use the state hook to store the state outside of this component.
  • React store all state in fiber tree, outside of component.

3th thing

Use hook at top level of our component,

import React, { useState } from "react";

const state = () => {
  const [visible, setVisisble] = useState(false);
	  const [isApproved, setIsApproved] = useState(false);
  let count = 0;
  const handleClick = () => {
    setVisisble(true);
    count++;
    console.log(visisble);
  };
  return (
    <div>
      <button onClick={handleClick}>Show</button>
    </div>
  );
};

export default state;

These ‘visible’ and ‘isApproved’ is just local identifiers. React is not aware of it.

  • we are just telling react that ‘I need to store 2 boolean value’
  • some where react is going to store that values most likely inside an array [false,true]
  • next time react re render component , its going to look at this array and grab 1st element and store its value inside visible varaible
  • React relies on order of these element store. so it can map
  • And that means we can not use hook inside if statement or inside any kinds of loops

Summary

  • React update state asynchronously
  • state is stored outside of component
  • use hooks at the top level of our component.

Choosing the state structure

Best practice for choosing the state structure.

import React, { useState } from "react";

const state = () => {
  const [firstName, setFirstName] = useState("Bishal");
  const [lastName, setLastName] = useState("Bomjan");
  const [loading,setLoading] = useState(false);
  const fullName = firstName + " " + lastName;
  return (
    <div>
      {firstName} {lastName} {fullName}
    </div>
  );
};

export default state;

Means we don’t use state to store fullName.

  • It can be store using combination of first and last name.

Above 1st 2 variable are relatable so better to combine them in person object.

import React, { useState } from "react";

const state = () => {
  const [person, setPerson] = useState({
    firstName: "bishal",
    lastName: "Bomjan",
    contact: {
      address: {
        street: "",
      },
    },
  });

  return <div></div>;
};

export default state;

we can also do that and even add nested structure like that but use simple structure of that.

Keep Component Pure

What is pure Component?

In Computer Science, a pure function is a function that, given the same input, it always return the same output.

  • If we call function 10 times and always return same output that is pure function.
  • But if we get different result at different times we say that function is impure.
  • React is design around this concept

How we keep our component pure?

Keep ant kind of changes out of the render phase. let see what I mean?

import React from "react";
let count = 0;
const Message = () => {
  return <div> Message {count}</div>;
};

export default Message;
const App = () => {
  return (
    <div>
      <Message />
      <Message />
      <Message />
    </div>
  );
};

export default App;

we get same output

  • now if we update variable as part of rendering like
import React from "react";
let count = 0;
const Message = () => {
  count++;
  return <div>Message {count}</div>;
};

export default Message;

image.png

  • so to keep component pure we should keep changes out of render phase.
  • we should not change any object that existed before rendering like count varible.

But, its totally fine to update an object that we create as part of rendering.

example

count variable inside message component.

import React from "react";
const Message = () => {
  let count = 0;
  count++;
  return <div>Message {count}</div>;
};

export default Message;

image.png

Understanding The Strict Mode

  • We can understand using impure example
  • we render component 3 time and ideally we should get result like 1,2,3 but we get 2,4,6

In main.tsx our app component is wrapped inside strict mode

  • It doesn’t have a representation
  • It is only use to catch potential problems. 1 of the problem in impure function.
  • In development mode strict mode enable, every thing render twice and 1st render is used primarily for deteciting potential issues with our code while 2nd render is used to actual update

Updating Objects

we have a drink state object with title americano and price and when we click on button price should change.

How to do that?

  • When dealing with object and array, we should treat them as immutable or read only.

  • We don’t directly update value like

    // drink.price = 6 // setDrink(drink) this is wrong way

  • Nothing will happen because react doesn’t detect any changes.

  • To tell react about state update we have to give react a brand new object as shown below.

import React, { useState } from "react";

const Message = () => {
  const [drink, setDrink] = useState({
    title: "Americano",
    price: 400,
  });
  const handleClick = () => {
    // drink.price = 6
    // setDrink(drink) this is wrong way
    const newDrink = { title: drink.title, price: 600 };
    setDrink(newDrink);
  };
  return (
    <div>
      {drink.title}
      {drink.price}
      <button onClick={handleClick}>Click Me</button>
    </div>
  );
};

export default Message;

  • In this example we have few properties. What if we have tons of properties. It will be tedious to copy all properties.
  • Instead of copying all these properties we can use spread operator.
const handleClick = () => {
    // drink.price = 6
    // setDrink(drink) this is wrong way
    const newDrink = { ...drink, price: 600 };
    setDrink(newDrink);
  };

or we don’t even need to create a separate object, we can do

const handleClick = () => {
    // drink.price = 6
    // setDrink(drink) this is wrong way
    setDrink({...drink,price:600);
  };

Summary:

In State object and array are immutable or read only.

Updating Nested Object

Let’s look at more complicated example

import React, { useState } from "react";

const Message = () => {
  const [customer, setCustomer] = useState({
    name: "Jhon",
    address: {
      city: "San franciso",
      zipCode: 94110,
    },
  });
  const handleChange = () => {
    setCustomer({
      ...customer,
      address: { ...customer.address, zipCode: 2121 },
    });
  };
  return (
    <div>
      <p>{customer.address.zipCode}</p>
      <button onClick={handleChange}>Click</button>
    </div>
  );
};

export default Message;

spread operator in JS is shallow, which means when we use it to copy customer object, its going to return existing address object in memory.

  • To solve this we need to set address to new object as above.
  • So when working with state object we should avoid deeply nested structure, because the deeper this structure gets, the more complicated our update logic will be.
  • Prefer flat structure rather then deeply nested structure.

Updating Array

  • Same concept applies to array
  • If we have an array, we should not mutate or change instead, we should give react a brand new array.

Example: Adding new element in Array

import React, { useState } from "react";

const Message = () => {
  const [tags, setTags] = useState(["happy", "cheerful"]);
  const handleClick = () => setTags([...tags, "exciting"]);
  return (
    <div>
      {tags.map((tag, index) => (
        <p key={index}>{tag}</p>
      ))}
      <button onClick={handleClick}>click</button>
    </div>
  );
};

export default Message;

Updating exiting element

const handleUpdate = () =>
    setTags(tags.map((tag, index) => (tag === "happy" ? "happier" : tag)));
     <button onClick={handleUpdate}>Update</button>

Removing element

  const handleDelete = () => {
    setTags(tags.filter((tags) => tags !== "happy"));
  };
      <button onClick={handleDelete}>Delete</button>

Updating Array of Objects

Slightly more complicated Example.

import React, { useState } from "react";

const Message = () => {
  const [bugs, setBugs] = useState([
    { id: 1, title: "bug 1", fixed: false },
    { id: 2, title: "bug 2", fixed: false },
  ]);
  const handleClick = (id: number) => {
    setBugs(bugs.map((bug) => (bug.id === id ? { ...bug, fixed: true } : bug)));
  };
  return (
    <div>
      {bugs.map((bug, index) => (
        <p key={index}>
          {bug.title} {bug.fixed ? "Fixed" : "Unfixed"}
          <button onClick={() => handleClick(bug.id)}>Click</button>
        </p>
      ))}
    </div>
  );
};

export default Message;

we can simplify update logic with immer.

Slightly more complicated Example.

import React, { useState } from "react";

const Message = () => {
  const [bugs, setBugs] = useState([
    { id: 1, title: "bug 1", fixed: false },
    { id: 2, title: "bug 2", fixed: false },
  ]);
  const handleClick = (id: number) => {
    setBugs(bugs.map((bug) => (bug.id === id ? { ...bug, fixed: true } : bug)));
  };
  return (
    <div>
      {bugs.map((bug, index) => (
        <p key={index}>
          {bug.title} {bug.fixed ? "Fixed" : "Unfixed"}
          <button onClick={() => handleClick(bug.id)}>Click</button>
        </p>
      ))}
    </div>
  );
};

export default Message;

we can simplify update logic with immer.

Slightly more complicated Example.

import React, { useState } from "react";

const Message = () => {
  const [bugs, setBugs] = useState([
    { id: 1, title: "bug 1", fixed: false },
    { id: 2, title: "bug 2", fixed: false },
  ]);
  const handleClick = (id: number) => {
    setBugs(bugs.map((bug) => (bug.id === id ? { ...bug, fixed: true } : bug)));
  };
  return (
    <div>
      {bugs.map((bug, index) => (
        <p key={index}>
          {bug.title} {bug.fixed ? "Fixed" : "Unfixed"}
          <button onClick={() => handleClick(bug.id)}>Click</button>
        </p>
      ))}
    </div>
  );
};

export default Message;

we can simplify update logic with immer.

Sharing State between component

some times we need to share state between compoents

for eg: Imagine we are going to build an e-commerce application.

  • we have navbar on top where we see total number of items in shoping cart.
  • Below that we have the shppping cart component.
  • Now user update or delete shopping cart item, it should reflect changes in navbar component.

How do we do this? We have lift state to closest parent.

Example

import React, { useState } from "react";
import Navbar from "./component/Navbar";
import Cart from "./component/Cart";

const App = () => {
  const [products, setProducts] = useState([
    "Product 1",
    "Product 3",
    "Product 3",
  ]);
  const handleClear = () => {
    setProducts([]);
  };
  return (
    <div>
      <Navbar count={products.length} />
      <Cart item={products} onClear={handleClear} />
    </div>
  );
};

export default App;

we are passing products as props to cart component.

Exercise: Updating state

we have game object with id and player which is an object with name property.

when user click button user set the name of plater to something else.

import React, { useState } from "react";

const App = () => {
  const [game, setGame] = useState({
    id: 1,
    player: { name: "John" },
  });
  const handleClick = () => {
    setGame({ ...game, player: { ...game.player, name: "Sita" } });
  };
  return (
    <div>
      {game.player.name}
      <button onClick={handleClick}>CLick</button>
    </div>
  );
};

export default App;

Exercise 3

we have shopping cart object with properties like discount which in number, items which array of object, In each object we have 3 properties id, title and quantity

when user click on button, we want to change the quantity of product to 2.

import React, { useState } from "react";

const App = () => {
  const [cart, setCart] = useState({
    discount: 0.1,
    items: [
      { id: 1, title: "Product 1", quantity: 1 },
      { id: 2, title: "Product 2", quantity: 1 },
    ],
  });
  const handleClick = (id: number) => {
    setCart({
      ...cart,
      items: cart.items.map((item) =>
        item.id === id ? { ...item, quantity: item.quantity + 1 } : item
      ),
    });
  };
  return (
    <div>
      <ul>
        {cart.items.map((item, index) => (
          <li key={index}>
            {item.title}
            {item.quantity}
            <button onClick={() => handleClick(item.id)}>Click</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default App;

Exercise 4

We pass long text and component will summarize the text for us.

import React, { useState } from "react";
interface Props {
  children: string;
  maxLen: number;
}
const Expand = ({ children, maxLen }: Props) => {
  let display;
  const [expand, setExpand] = useState(false);
  if (children.length <= maxLen) return <p>{children}</p>;
  display = expand ? children : children.slice(0, maxLen);
  return (
    <div>
      {display}
      <button onClick={() => setExpand(!expand)}>
        {expand === true ? "Less" : "More"}
      </button>
    </div>
  );
};

export default Expand;

In App.tsx

import React from "react";
import Expand from "./component/Expand";

const App = () => {
  return (
    <div>
      <Expand maxLen={10}>
        Lorem ipsum dolor sit amet consectetur, adipisicing elit. Eius, minus
        quas itaque neque at voluptatem quasi! Quaerat cum maiores modi ex vero
        consectetur tempora, neque suscipit nulla saepe perspiciatis eos
        deleniti voluptate ducimus dolorum voluptas corrupti architecto
        voluptatibus delectus corporis. Illo ducimus est voluptate! Qui
        repellendus quas optio aperiam quisquam necessitatibus commodi deserunt
        natus fugit. Quaerat mollitia consectetur corrupti rerum, sint minima
        aspernatur rem ipsam, sit dolorem officia omnis eaque eos repudiandae,
        accusamus ipsum sapiente voluptas error ut possimus ullam recusandae!
        Cupiditate necessitatibus mollitia perferendis. Asperiores, facere ex
        quasi architecto vitae consectetur placeat cupiditate tenetur possimus
        cumque ducimus, quis sed dignissimos velit, ut fugit. Officiis ratione
        architecto, vero illo, quisquam cumque in tempore rem reprehenderit
        minus ex praesentium odio laborum, molestias temporibus! Vitae alias
        nihil reiciendis quae accusantium labore eligendi beatae sint. Libero,
        magnam molestiae exercitationem repellat nobis asperiores nihil minus
        reprehenderit labore, voluptates unde necessitatibus. Soluta debitis
        quas accusantium iste harum aliquid, dolorum, ducimus beatae doloremque
        ratione consequuntur libero enim incidunt excepturi autem dolorem nemo
        tempora perferendis nobis expedita. Ullam quia cumque, ratione magni,
        maiores vel recusandae rem, minus nostrum veniam iure officia nemo
        soluta necessitatibus quibusdam tempora ea nesciunt illum? Commodi animi
        hic molestias quam, pariatur, quos fuga doloremque facilis dolorem,
        accusamus excepturi molestiae ut veritatis laudantium nisi. Consequatur
        sequi vitae delectus nihil corporis tenetur saepe commodi nemo, vero
        minus nesciunt error? Eveniet recusandae inventore delectus omnis
        deserunt autem minima consequatur qui. Nobis, debitis corporis eius
        eveniet temporibus impedit quae deserunt quasi pariatur odit ratione.
        Hic officiis dolore maxime illum eius repudiandae cum nobis ipsam soluta
        ut illo nisi earum dignissimos, voluptatum rem repellendus aliquam atque
        temporibus delectus dolorum quidem maiores. Neque dolore ipsum libero.
        Ea dolorem amet voluptate quas non numquam eligendi natus dignissimos
        unde facilis officia enim, explicabo facere earum dolore nesciunt
        reprehenderit aliquid velit nihil!
      </Expand>
    </div>
  );
};

export default App;

Forms

Introduction

From are essential part of many web application.

  • we will React hook form and zod for managing form

Building a form

  • 1st create a form. we use bootstrap to create form. Bring the import in main.tsx

Just create a simple form

import React from "react";

const Form = () => {
  return (
    <form>
      <div className="mb-3">
        <label htmlFor="name" className="form-label">
          Name
        </label>
        <input type="text" id="name" className="form-control" />
      </div>
      <div className="mb-3">
        <label htmlFor="age" className="form-label">
          Age
        </label>
        <input type="number" id="age" className="form-control" />
        <button className="btn btn-primary" type="submit">
          Sumbit
        </button>
      </div>
    </form>
  );
};

export default Form;

Handling form submission

to handle form submit we have onSubmit props in form like.

but when form is submitted it send data to server so whole page is reload.

  • to solve this problem we need to prevent this default behaviors.
import React from "react";

const Form = () => {
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault(); // prevent default behaviour
        console.log("Submitted.");
      }}
    >
      <div className="mb-3">
        <label htmlFor="name" className="form-label">
          Name
        </label>
        <input type="text" id="name" className="form-control" />
      </div>
      <div className="mb-3">
        <label htmlFor="age" className="form-label">
          Age
        </label>
        <input type="number" id="age" className="form-control" />
        <button className="btn btn-primary" type="submit">
          Sumbit
        </button>
      </div>
    </form>
  );
};

export default Form;

Now submitted is loged in console

  • In real world we need to send form data to server. and we will see it later.

Accessing Input Fields

We will learn how to use ‘state’ hook to get form data.

we will see ref hook to get form data.

import React, { useRef } from "react";

const Form = () => {
  const [person, setPerson] = useState({
    name: "",
    age: 0,
  });
  const nameref = useRef<HTMLInputElement>(null);
  // we have to assign null to ref hook because at first we don't have any element
  //so ref confuse which dom element it is refrencing.
  const ageref = useRef<HTMLInputElement>(null);
  // and also have to tell what type of element it is refrencing.
  // In this case HTML input element
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        console.log("Submitted.");
        //here we have to chek ref is it is still null or not
        // and ref hook only have current, which return html dom itself
        // and we get values property of dom.
        // nameref.current.value;
        // we can do
        if (nameref.current !== null) person.name = nameref.current.value;
        if (ageref.current !== null)
          //we get age as string so need to change to number
          person.age = Number(ageref.current.value);
        console.log(person);
      }}
    >
      <div className="mb-3">
        <label htmlFor="name" className="form-label">
          Name
        </label>
        <input ref={nameref} type="text" id="name" className="form-control" />
      </div>
      <div className="mb-3">
        <label htmlFor="age" className="form-label">
          Age
        </label>
        <input ref={ageref} type="number" id="age" className="form-control" />
        <button className="btn btn-primary" type="submit">
          Sumbit
        </button>
      </div>
    </form>
  );
};

export default Form;

Controlled Component or useState for From data

Instead of useRef, we can use the state hook.

onChange prop can be use as below

import { useState } from "react";

const Form = () => {
  const [person, setPerson] = useState({
    name: "",
    age: 0,
  });

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        console.log(person);
      }}
    >
      <div className="mb-3">
        <label htmlFor="name" className="form-label">
          Name
        </label>
        <input
          onChange={(e) => setPerson({ ...person, name: e.target.value })}
          type="text"
          id="name"
          className="form-control"
        />
      </div>
      <div className="mb-3">
        <label htmlFor="age" className="form-label">
          Age
        </label>
        <input
          onChange={(e) =>
            setPerson({ ...person, age: parseInt(e.target.value) })
          }
          type="number"
          id="age"
          className="form-control"
        />
        <button className="btn btn-primary" type="submit">
          Sumbit
        </button>
      </div>
    </form>
  );
};

export default Form;

import { useState } from "react";

const Form = () => {
  const [person, setPerson] = useState({
    name: "",
    age: 0,
  });

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        console.log(person);
      }}
    >
      <div className="mb-3">
        <label htmlFor="name" className="form-label">
          Name
        </label>
        <input
          onChange={(e) => setPerson({ ...person, name: e.target.value })}
          type="text"
          id="name"
          className="form-control"
        />
      </div>
      <div className="mb-3">
        <label htmlFor="age" className="form-label">
          Age
        </label>
        <input
          onChange={(e) =>
            setPerson({ ...person, age: parseInt(e.target.value) })
          }
          type="number"
          id="age"
          className="form-control"
        />
        <button className="btn btn-primary" type="submit">
          Sumbit
        </button>
      </div>
    </form>
  );
};

export default Form;
import { useState } from "react";

const Form = () => {
  const [person, setPerson] = useState({
    name: "",
    age: 0,
  });

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        console.log(person);
      }}
    >
      <div className="mb-3">
        <label htmlFor="name" className="form-label">
          Name
        </label>
        <input
          onChange={(e) => setPerson({ ...person, name: e.target.value })}
          type="text"
          id="name"
          className="form-control"
        />
      </div>
      <div className="mb-3">
        <label htmlFor="age" className="form-label">
          Age
        </label>
        <input
          onChange={(e) =>
            setPerson({ ...person, age: parseInt(e.target.value) })
          }
          type="number"
          id="age"
          className="form-control"
        />
        <button className="btn btn-primary" type="submit">
          Sumbit
        </button>
      </div>
    </form>
  );
};

export default Form;

we have to set value here to person object value to create single source of truth

<input
          onChange={(e) => setPerson({ ...person, name: e.target.value })}
          type="text"
          id="name"
          value={person.name}
          className="form-control"
        />
<input
          onChange={(e) => setPerson({ ...person, name: e.target.value })}
          type="text"
          id="name"
          value={person.name}
          className="form-control"
        />

but with every typing we change state.

so, we render but it is easier than ref hook

so for simple form we can use useState hook.

so it doesn’t affect perfromance but large application we use ref

Managing Form with react hook form

As our form become complex, managing form with state hook can become time consuming and error prone

  • this is where we use popular libary called react-form-hook
  • With this we can quickly build forms with less code
  • In terminal do npm i react-hook-form
  • we can use useFrom from react-hook-form

const {register,handleSubmit} = useForm()

useform return object with bunch of properties and we can directly restructure them.

Validation in react-hook-form

import { useState } from "react"; import { useForm } from "react-hook-form"; // this is need to tell useForm state //what is the type of form and also get suggesstion // when we interface FormInterface { name: string; age: number; } const Form = () => { const { register, handleSubmit, formState: { errors }, } = useForm<FormInterface>(); const [person, setPerson] = useState({ name: "", age: 0, }); console.log(register("name")); // register return bunch of properties of object // Like onBlur, name onChange,ref // so it work using refhook under the hood. return ( <form // onSubmit also clean we don't have preventDefault. // It is implmented inder the hood onSubmit={handleSubmit((data) => { console.log(data); })} > <div className="mb-3"> <label htmlFor="name" className="form-label"> Name </label> {/* we can apply validation rule like */} <input {...register("name", { required: true, minLength: 3 })} type="text" id="name" className="form-control" /> {errors.name?.type === "required" && ( <p className="text-danger">Name is required Field.</p> )} {errors.name?.type === "minLength" && ( <p className="text-danger">Name must be atleast 3 character.</p> )} </div> <div className="mb-3"> <label htmlFor="age" className="form-label"> Age </label> {/* for number we have to give valueAsNumber to true */} <input {...register("age", { required: true, valueAsNumber: true, min: 18 })} type="text" id="name" className="form-control" /> {errors.age?.type === "required" && ( <p className="text-danger">Age is required Field.</p> )} {errors.age?.type === "min" && ( <p className="text-danger">Age must be atleast 18.</p> )} </div> <button className="btn btn-primary" type="submit"> Sumbit </button> </form> ); }; export default Form;

Schema based Validation with zod.

  • Currently we have a couple of validation rules in middle of our markup.
  • As our form get more complex, we will end up with lot of validation rules all over the place.
  • So its better to use technique called schema based validation.

Various libaries out there that allows to define validation

  • Joi
  • yup
  • zod

we will use zod form validation. 1 of popular validation rule

need to install zod npm i zod

nedd to install resolver like npm i @hookform/resovers/zod

import { useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
const schema = z.object({
  name: z.string().min(3, { message: "Name must be atleast 3 character." }),
  age: z
    .number({ invalid_type_error: "Age is required field" })
    .min(18, { message: "Age must be atleast 18." }),
});
type FormData = z.infer<typeof schema>;
const FormZod = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FormData>({ resolver: zodResolver(schema) });

  //   register return bunch of properties of object
  // Like onBlur, name onChange,ref
  // so it work using refhook under the hood.

  return (
    <form
      // onSubmit also clean we don't have preventDefault.
      // It is implmented inder the hood
      onSubmit={handleSubmit((data) => {
        console.log(data);
      })}
    >
      <div className="mb-3">
        <label htmlFor="name" className="form-label">
          Name
        </label>
        {/* we can apply validation rule like */}
        <input
          {...register("name")}
          type="text"
          id="name"
          className="form-control"
        />
        {errors.name && <p className="text-danger">{errors.name.message}</p>}
      </div>
      <div className="mb-3">
        <label htmlFor="age" className="form-label">
          Age
        </label>
        {/* for number we have to give valueAsNumber to true */}
        <input
        //remove validation from react hook from here.
          {...register("age")}
          type="text"
          id="name"
          className="form-control"
        />
        //remove extra validation here.
        {errors.age && <p className="text-danger">{errors.age.message}</p>}
      </div>
      <button className="btn btn-primary" type="submit">
        Sumbit
      </button>
    </form>
  );
};

export default FormZod;

Disabling Submit Button

formState have isValid property

so if our form is not valid we can disable submit button

 const {
    register,
    handleSubmit,
    formState: { errors, isValid },
  } = useForm<FormData>({ resolver: zodResolver(schema) });
<button disabled={!isValid} className="btn btn-primary" type="submit">
        Sumbit
      </button>

Connecting to Backend

Understanding the Effect hook

Before we talk about connecting apps to backend, we need to understand useEffect hook in react.

  • Early we discuss that react component should be pure function.
  • A pure function should not have any side effects and should return the same result if we give it the same input
  • To keep component pure → keep changes out of render phase.
  • There are situation where we might store some data in local storage of browser so we can remember it in future.
  • Or may need to call server to fetch or save the data.
  • Or manually modify the DOM Element.
  • None of these situations are about rendering a component they don’t return return JSX markup. So where we can implement them?

Note :-That where the effect hook comes in . With effect hook we tell react → execute a piece of code after component is rendered.

Example

Why your Effect is not pure ?

  • The line ref.current?.focus() runs every render.
  • This causes a side effect (changing focus in the browser).
  • Side effects inside the render phase break purity.

So this component is impure because it changes the DOM during render.

Questions

When does ref.current?.focus() run?

  • useRef() creates an object like { current: null }.

  • During the first render, the <input> has not been mounted yet, so ref.current is still null.

    ref.current?.focus() does nothing on the first render.

  • On subsequent renders, ref.current may already point to the <input> (since React sets it after commit), so focus() can run during render.


🔹 Why is this a problem?

Even if it sometimes does nothing (on the first render), you’re still calling an imperative DOM method inside the render phase.

That breaks React’s pure render principle, which says:

The render function should be a pure mapping from props/state → UI, with no side effects.

Calling focus() is a side effect because it:

  • Mutates the DOM.
  • Isn’t guaranteed to be stable (React may render multiple times before committing in Strict Mode).

This is why React tells us to move such logic into useEffect, which runs after the DOM is updated.

import React, { useEffect, useRef } from "react";

const Effect = () => {
  const ref = useRef<HTMLInputElement>(null);
  ref.current?.focus(); // does nothing on 1st render.
  return (
    <div>
      <label htmlFor="id" className="form-label">
        Input
      </label>
      <input id="id" className="form-contorl" ref={ref} type="text" />
    </div>
  );
};

export default Effect;

Problem statement simple explanation

Think of it like this:

  1. Pure component = recipe

    • A pure component is like a cooking recipe.
    • Same ingredients (props/state) → always same dish (JSX).
    • It doesn’t try to turn on the stove or move plates while just writing the recipe.
  2. Your component

    ref.current?.focus();
    
    
    • This line is like trying to move a plate onto the table while still writing the recipe.
    • Even if the plate isn’t there yet (ref.current is null), you tried to move it.
  3. Result

    • Right now, nothing happens (because the plate isn’t there yet).
    • But the component is still impure, because it tries to do a side effect during rendering.

✅ Super simple rule

  • If your component’s render only returns JSXpure.
  • If your component’s render tries to do something with the DOM (like focus(), scroll(), console.log() side effects) → impure, even if it doesn’t succeed.

👉 So your component is not pure, because it tries to focus inside render.

If you move the focus() into useEffect, then it becomes pure.

Understanding sideeffect

Pure component

import React, { useEffect, useRef } from "react";

const Effect = () => {
  const ref = useRef<HTMLInputElement>(null);
  useEffect(() => {
    ref.current?.focus();
  });

  return (
    <div>
      <label htmlFor="id" className="form-label">
        Input
      </label>
      <input id="id" className="form-contorl" ref={ref} type="text" />
    </div>
  );
};

export default Effect;

Why is this now pure?

  • The render part (return (...)) only produces JSX.
  • It doesn’t try to change the DOM during render.
  • The focus side effect is moved into useEffect, which React runs after the component is painted on the screen.

So the render function is pure (just math: props/state → JSX).

All “extra actions” happen later in useEffect.


Simple takeaway:

  • Pure component = only returns UI.
  • Side effects = go inside useEffect.

Few important things to know about effect hook

  • just like useState and useRef hook we can call it at top level of our components
  • We can call it multiple times for different purposes
  • Example :- right after we can do
  useEffect(() => {
    document.title = "MyApp";
  });
  • we have 2 seperate effect hook, about focusing the form and about setting title
  • these 2 have different separate responsibilities.

Comments

Popular posts from this blog

Redux toolkit

Array Advance

HTML – a must-know for building responsive and dynamic layouts.