From 9cc04a5e0efbd8230a411bde34c565a3171d29c0 Mon Sep 17 00:00:00 2001 From: Syed Abdullah Ali Date: Thu, 10 Jul 2025 17:22:17 +0530 Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20Completed=20understanding=20of=20Re?= =?UTF-8?q?act=20lifecycle=20in=20both=20Function=20and=20Class=20Componen?= =?UTF-8?q?ts=20=F0=9F=94=84=F0=9F=93=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/component/ClassComponent.tsx | 116 +++++++++-- client/src/component/FunctionComponent.tsx | 223 ++++++++++++--------- client/src/{api => services}/crud.ts | 2 - client/src/ui/Table.tsx | 31 +-- client/src/wrapper/HookWrapper.tsx | 2 +- 5 files changed, 243 insertions(+), 131 deletions(-) rename client/src/{api => services}/crud.ts (99%) diff --git a/client/src/component/ClassComponent.tsx b/client/src/component/ClassComponent.tsx index d1f12b8..3ec9f62 100644 --- a/client/src/component/ClassComponent.tsx +++ b/client/src/component/ClassComponent.tsx @@ -1,26 +1,108 @@ -import { Component } from "react"; -import { UserFetchApi } from "../type/crud"; +import React, { Component } from "react"; import Table from "../ui/Table"; -import { User } from "../type/crud"; -interface Props{ - result:UserFetchApi +import { User, UserFetchApi } from "../type/crud"; + +interface Props { + result: UserFetchApi; +} + +interface Column { + headerName: string; + accessor: keyof T; } -class ClassComponent extends Component{ - render() { - interface Column { - headerName: string; - accessor: keyof T; - } - - const columnData: Column[] = [ +type State = { + users: User[]; + status: "idle" | "loading" | "success" | "error"; + count: number; +}; + +class ClassComponent extends Component { + constructor(props: Props) { + // ๐Ÿ”ง Mounting Phase: constructor + // Called once when the component is created. + // Best place to initialize `state` and bind methods. + super(props); + console.log("๐Ÿ”ง constructor: called once before component mounts"); + + this.state = { + users: [], + status: "idle", + count: 0, + }; + } + + static getDerivedStateFromProps(nextProps: Props, prevState: State) { + // ๐Ÿ“ฅ Called on every render (both mount and update) + // Use to derive state from props. + // Rarely needed. Avoid setting state here unless necessary. + console.log("๐Ÿ“ฅ getDerivedStateFromProps"); + return null; + } + + componentDidMount(): void { + // โœ… Mounting Phase: componentDidMount + // Called once, right after the component is added to the DOM. + // Ideal for API calls, subscriptions, etc. + console.log("โœ… componentDidMount: component is now in the DOM"); + } + + shouldComponentUpdate(nextProps: Props, nextState: State): boolean { + // ๐Ÿ” Updating Phase: shouldComponentUpdate + // Use this to prevent unnecessary renders. + // Return false to skip re-render. + console.log("๐Ÿ” shouldComponentUpdate"); + return true; // default is true + } + + getSnapshotBeforeUpdate(prevProps: Props, prevState: State) { + // ๐Ÿ“ธ Called right before DOM updates + // Used to capture scroll positions, etc., before DOM changes + console.log("๐Ÿ“ธ getSnapshotBeforeUpdate"); + return null; + } + + componentDidUpdate(prevProps: Props, prevState: State, snapshot: any) { + // ๐Ÿ” Updating Phase: componentDidUpdate + // Called after every re-render caused by state or props change + console.log("๐Ÿ” componentDidUpdate"); + } + + componentWillUnmount(): void { + // ๐Ÿงน Unmounting Phase: componentWillUnmount + // Called once, just before the component is removed from the DOM + // Ideal for cleanup: timers, subscriptions, event listeners + console.log( + "๐Ÿงน componentWillUnmount: cleanup before component is removed from DOM" + ); + + // โš ๏ธ NOTE: + // In dev, React Fast Refresh (HMR) may unmount/remount your component automatically, + // causing this to run on every save/hot update. This is expected during development. + } + + render() { + // ๐ŸŽจ Called during Mounting and Updating + // Should return JSX โ€” it must be a pure function (no side effects). + console.log( + `[DEBUG] ๐ŸŽจ render() at ${new Date().toLocaleTimeString()}` + ); + + const columnData: Column[] = [ { headerName: "Name", accessor: "name" }, { headerName: "Email", accessor: "email" }, { headerName: "City", accessor: "city" }, ]; - const {data,getDataofUser,status} = this.props.result; - return columns={columnData} data={data.data}/> - } + + const { data } = this.props.result; + + return ( +
+

React Lifecycle Demo (Class Component)

+ columns={columnData} data={data.data} /> +
+ ); + } } -export default ClassComponent +export default ClassComponent; diff --git a/client/src/component/FunctionComponent.tsx b/client/src/component/FunctionComponent.tsx index 093cf30..2c4ac51 100644 --- a/client/src/component/FunctionComponent.tsx +++ b/client/src/component/FunctionComponent.tsx @@ -1,120 +1,151 @@ -// import logo from './logo.svg'; -import { ChangeEvent,useState } from 'react'; -import { useHookGetData,useHookDeleteData, useHookPostData, useHookUpdateData } from '../api/crud'; -import { User, UserFetchApi } from '../type/crud'; +import { ChangeEvent, useEffect, useState } from "react"; +import { + useHookGetData, + useHookDeleteData, + useHookPostData, + useHookUpdateData, +} from "../services/crud"; +import { User, UserFetchApi } from "../type/crud"; function FunctionComponent() { - const result:UserFetchApi = useHookGetData() - const {status,data,getDataofUser} =result - - const {deleteDataofUser} = useHookDeleteData() - const {createDataofUser} = useHookPostData() - const {updateDataofUser} = useHookUpdateData() - - const userObj = { - name:"", - email:"", - city:"", - } + // ๐Ÿง  useState is the Function Component equivalent of this.state in class components + const userObj = { name: "", email: "", city: "" }; + const [form, setForm] = useState({ ...userObj }); + const [isEditId, setEditId] = useState(""); - const [form,setForm] = useState({...userObj}) - const [isEditId,setEditId] = useState('') + // ๐Ÿง  Custom hooks managing API data (similar to componentDidMount + data logic) + const result: UserFetchApi = useHookGetData(); + const { status, data, getDataofUser } = result; + const { deleteDataofUser } = useHookDeleteData(); + const { createDataofUser } = useHookPostData(); + const { updateDataofUser } = useHookUpdateData(); - const handaledeleteuserData = async (id:string)=>{ - try{ - await deleteDataofUser(id) - getDataofUser() - }catch (error){ - console.log(error) - } - } + // ๐Ÿ”„ useEffect = lifecycle in functional components + // โœ… Runs once when component is mounted (like componentDidMount) + useEffect(() => { + console.log("โœ… useEffect โ†’ acts like componentDidMount"); + getDataofUser(); - const handaleUpdateUserData = async ()=>{ - try{ - console.log('isEditId',isEditId) - await updateDataofUser(isEditId,form) - setForm({...userObj}) - setEditId('') - }catch (error){ - console.log(error) - } - } + return () => { + // ๐Ÿงน Cleanup function = componentWillUnmount + console.log("๐Ÿงน Cleanup โ†’ acts like componentWillUnmount"); + }; + }, []); // [] = only once (mount + unmount) - const handalesaveUsreData = async (e:ChangeEvent)=>{ - e.preventDefault() - try{ - if(isEditId){ - await handaleUpdateUserData() - }else{ - await createDataofUser(form) + const handleDeleteUserData = async (id: string) => { + try { + await deleteDataofUser(id); + getDataofUser(); + } catch (error) { + console.log(error); } - await getDataofUser() + }; - }catch(error){ - console.log(error) - } - } + const handleUpdateUserData = async () => { + try { + console.log("Editing ID โ†’", isEditId); + await updateDataofUser(isEditId, form); + setForm({ ...userObj }); + setEditId(""); + } catch (error) { + console.log(error); + } + }; + const handleSaveUserData = async (e: ChangeEvent) => { + e.preventDefault(); + try { + if (isEditId) { + await handleUpdateUserData(); + } else { + await createDataofUser(form); + } + getDataofUser(); + } catch (error) { + console.log(error); + } + }; + const handleUpdate = (obj: User) => { + setEditId(obj._id); + setForm({ name: obj.name, email: obj.email, city: obj.city }); + }; - const handaleUpdate = (obj:User)=>{ - setEditId(obj._id) - setForm({name:obj.name,email:obj.email,city:obj.city}) - } + const handleChange = (e: ChangeEvent) => { + setForm((prev) => ({ ...prev, [e.target.name]: e.target.value })); + }; - const handaleChnage = (e:ChangeEvent)=>{ - setForm(prev=>({...prev,[e.target.name]:e.target.value})) - } return (
-
- - - - {isEditId? - <> - - - : - - } + + + + + + {isEditId ? ( + <> + + + + ) : ( + + )}
-
- { - +
+
- - - - - - + + + + + + + - { - status&&data.data.map((el:User)=> - - - - - - - - ) - } - + {status && + data.data.map((el: User) => ( + + + + + + + + ))} +
NameEmailCityDateAction
NameEmailCityDateAction
{el.name}{el.email}{el.city}{new Date(el.createdAt).toLocaleString()} - - -
{el.name}{el.email}{el.city}{new Date(el.createdAt).toLocaleString()} + + +
- - } -
- +
); } diff --git a/client/src/api/crud.ts b/client/src/services/crud.ts similarity index 99% rename from client/src/api/crud.ts rename to client/src/services/crud.ts index c7db903..30e5238 100644 --- a/client/src/api/crud.ts +++ b/client/src/services/crud.ts @@ -1,8 +1,6 @@ import { useEffect, useState } from "react" import { CleanUser, UserFetchApi } from "../type/crud" - - function useHookGetData():UserFetchApi { const [data,setData] = useState({status:false,data:[]}) const getDataofUser = async ()=>{ diff --git a/client/src/ui/Table.tsx b/client/src/ui/Table.tsx index e76ce22..a5f830c 100644 --- a/client/src/ui/Table.tsx +++ b/client/src/ui/Table.tsx @@ -6,13 +6,12 @@ export interface Column { render?: (row: T) => ReactNode; } - - interface TabelProps{ - data:T[], - // columns:Column[], //not T[] - columns: Column[]; // Strongly typed! - mutation?:boolean - } +interface TabelProps { + data: T[]; + // columns:Column[], //not T[] + columns: Column[]; // Strongly typed! + mutation?: boolean; +} class Table extends Component> { render() { @@ -20,16 +19,18 @@ class Table extends Component> { return ( - {columns.map((el) => ( - - ))} + + {columns.map((el, i) => ( + + ))} + - {data.map((row) => ( - - {columns.map((col) => ( - + {columns.map((col, i) => ( + ))} {mutation && ( diff --git a/client/src/wrapper/HookWrapper.tsx b/client/src/wrapper/HookWrapper.tsx index 97559df..70e210b 100644 --- a/client/src/wrapper/HookWrapper.tsx +++ b/client/src/wrapper/HookWrapper.tsx @@ -1,5 +1,5 @@ import { ComponentType, JSX } from "react" -import { useHookGetData } from "../api/crud" +import { useHookGetData } from "../services/crud" import { UserFetchApi } from "../type/crud" interface HookProps {
{el.headerName}
{el.headerName}
- {col.render ? col.render(row) : String(row[col.accessor])} + {data.map((row, i) => ( +
+ {col.render ? col.render(row) : String(row[col.accessor])}