Header Ads

Header ADS

React important tropics

 নিচে React-এর উল্লেখ করা প্রতিটি টপিক বাংলায় বিস্তারিতভাবে ব্যাখ্যা করে কোড উদাহরণসহ দিলাম। আমি চেষ্টা করেছি বাস্তবজীবনের রিয়েল-ওয়ার্কফ্লো এবং পারফরম্যান্স-প্যাটার্নগুলোও দিক।

1) Hook কিভাবে (কখন/কেন) ব্যবহার করে — Custom Hook কেমন বানাবেন

Hook মূলত function component-এ state ও lifecycle logic ব্যবহার করার উপায়। প্রধান built-in hooks: useState, useEffect, useRef, useMemo, useCallback, useReducer ইত্যাদি।

কখন/কেন:

  • যখন component-এ local state দরকার — useState.

  • যখন side-effect (API call, subscription, DOM updates) দরকার — useEffect.

  • ব্যার্কার হিসেবে মেমো/ক্যাশ দরকার — useMemo/useCallback.

  • জটিল state transitions হলে — useReducer.

  • DOM node রেফার করতে — useRef.

Custom Hook = reusable logic
যদি একাধিক component একই ধরনের logic শেয়ার করে (API fetch, form handling, localStorage sync), সেটা extract করে custom hook বানান। custom hook নাম অবশ্যই use দিয়ে শুরু হবে (useFetch, useAuth ইত্যাদি)। custom hook React rule মেনে চলবে — hooks only at top level।

উদাহরণ — সহজ useFetch custom hook:

// useFetch.js import { useState, useEffect, useRef } from "react"; export function useFetch(url, options = {}) { const [data, setData] = useState(null); const [loading, setLoading] = useState(Boolean(url)); const [error, setError] = useState(null); const abortCtrlRef = useRef(); useEffect(() => { if (!url) return; const controller = new AbortController(); abortCtrlRef.current = controller; setLoading(true); setError(null); fetch(url, { signal: controller.signal, ...options }) .then(res => { if (!res.ok) throw new Error("Network response not ok"); return res.json(); }) .then(json => setData(json)) .catch(err => { if (err.name !== "AbortError") setError(err); }) .finally(() => setLoading(false)); return () => { controller.abort(); }; }, [url, JSON.stringify(options)]); // options stringify only if small; beware performance return { data, loading, error, abort: () => abortCtrlRef.current?.abort() }; }

ব্যবহার:

import React from "react"; import { useFetch } from "./useFetch"; function UsersList() { const { data: users, loading, error } = useFetch("/api/users"); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <ul> {users.map(u => <li key={u.id}>{u.name}</li>)} </ul> ); }

2) useEffect এর ব্যবহার — patterns & pitfalls

useEffect হচ্ছে side-effects handle করার hook। signature: useEffect(effectFn, depsArray?).

প্যাটার্নস:

  • Mount only: useEffect(()=>{ /* run once */ }, []) — componentDidMount মত।

  • Deps change: useEffect(()=>{/* run when x changes */}, [x]).

  • Cleanup: effect ফাংশন একটি cleanup function return করতে পারে — unsubscribe, clear timers।

  • Async inside effect: effect নিজে async করা যাবে না (effect expects sync return for cleanup). তাই ভিতরে async function বানান।

Common examples:

  1. Fetch on mount:

useEffect(() => { let mounted = true; fetch("/api") .then(r => r.json()) .then(data => { if (mounted) setData(data); }); return () => { mounted = false; }; }, []);
  1. Debounced effect:

useEffect(() => { const id = setTimeout(() => doSearch(query), 300); return () => clearTimeout(id); }, [query]);
  1. Subscription with cleanup:

useEffect(() => { const handler = msg => setMessages(prev => [...prev, msg]); socket.on("message", handler); return () => socket.off("message", handler); }, [socket]);

Pitfalls & best practices:

  • Missing deps: eslint rule (react-hooks/exhaustive-deps) সাহায্য করে—সব external variables effect এর dep list-এ দিন। না হলে stale closure বা bug হবে।

  • Over-fetching: যদি deps দ্রুত পরিবর্তিত হয়, effect বারবার চলবে — debounce বা useRef কৌশল ব্যবহার করুন।

  • Race conditions: পরে আসা fetch আগের রেজাল্ট overwrite করতে পারে; use AbortController বা mounted flag ব্যবহার করুন।

  • Avoid heavy work directly: CPU-heavy কাজ effect এ সরাসরি না করে web worker বা memoization ব্যবহার করুন।

  • Don’t change state unconditionally without deps: e.g., effect->setState->re-render->effect triggers again => infinite loop। এই ক্ষেত্রে deps ঠিক করুন।

3) Component rendering performance — সাবধানে কী করবেন

প্রধান লক্ষ্য: অপ্রয়োজনীয় re-renders কমানো

টেকনিকস:

  1. Split components

    • বড় component কে ছোট করে দিন। ছোট component-এ শুধু প্রয়োজনীয় props পাঠান। এক ছোট পরিবর্তন পুরো UI রি-রেন্ডার কমায়।

  2. React.memo

    • functional component memoize করে — props একই থাকলে re-render skip।

const Item = React.memo(function Item({ item, onClick }) { // only re-renders when props change shallowly return <div onClick={() => onClick(item.id)}>{item.name}</div>; });
  • যদি prop object বা function থাকে, ensure stable identity (useMemo/useCallback) বা provide custom comparison.

  1. useCallback এবং useMemo

    • useCallback functions কে stable রাখে (prevent child re-render if function prop changes).

    • useMemo expensive value compute cache করে।

const handleClick = useCallback((id) => { ... }, [deps]); const computed = useMemo(() => expensiveCalc(a,b), [a,b]);
  • সতর্কতা: সব জায়গায় useMemo/useCallback লাগবে না — profiling আগে ব্যবহার করুন।

  1. Keys & list rendering

    • list render এ unique, stable key ব্যবহার করুন। index কেবলমাত্র static lists এ ব্যবহার করা উচিত নয়।

  2. Avoid inline objects/functions in JSX

    • <Child onClick={() => doSomething(a)} data={{x}} /> প্রতিবার নতুন prop পাঠায়। পরিবর্তে useCallback/useMemo.

  3. Virtualization for long lists

    • react-window বা react-virtualized ব্যবহার করুন যদি লক্ষাধিক আইটেম থাকে।

  4. Avoid unnecessary state derived from props

    • derive on render or memoize—duplicate sources of truth এড়ান।

  5. Batching updates

    • React automatically batches in event handlers; in async callbacks use unstable_batchedUpdates (rare) or React 18 behavior covers most cases.

  6. Profile with React DevTools Profiler

    • কোন component বেশি সময় নিচ্ছে, কোথায় re-render হচ্ছে — আগে profile, পরে optimize.

  7. CSS and DOM size

  • অনেক DOM elements থাকলে render slow হয় — minimize DOM depth/size.

4) SOLID Principles applied to React (simplified)

SOLID মূলত OOP pattern, কিন্তু UI components-এও উপকারী:

  • S — Single Responsibility Principle (SRP)

    • প্রতিটি component একটি কাজ করুক। (e.g., UserList data fetch করে, UserItem presentation করে)

  • O — Open/Closed Principle

    • components extendable সেইভাবে লিখুন যাতে নতুন behavior add করতে props বা composition ব্যবহার করা যায়—modify না করে extend করা যায়। e.g., render prop বা children pattern।

  • L — Liskov Substitution Principle

    • যেসব components interchangeable হবে, তাদের contract (props shape) compatible রাখুন। (অর্থাৎ child class/component parent এর জায়গায় substitute করলে bug না হবে)

  • I — Interface Segregation Principle

    • বড় props object না দিয়ে ছোট, স্পষ্ট props রাখুন—components কেবল তাদের প্রয়োজনীয় props পাবে।

  • D — Dependency Inversion Principle

    • নির্ভরতাগুলো (e.g., services, API clients) component-এ 직접 বানানো না করে বাইরের দিকে inject করুন (context/provider বা props দিয়ে)। এভাবে টেস্টিং সহজ হয়।

উদাহরণ — SRP ও composition:

function UsersContainer() { const { data, loading } = useFetch("/api/users"); return <UsersList users={data} loading={loading} />; } function UsersList({ users = [], loading }) { if (loading) return <div>Loading...</div>; return users.map(u => <UserItem key={u.id} user={u} />); }

5) Global state — Zustand, Jotai, অন্যান্য জনপ্রিয় লাইব্রেরি (সমালোচনায়)

কখন global state দরকার?

  • অনেক দূরবর্তী Komponenten-এ থাকা state (auth, theme, cart, feature flags) — কিন্তু সবকিছু global করার দরকার নেই। প্রথমে consider local state, context, props drilling কমাতে লিফট আপ।

Popular options (সংক্ষেপে):

  • Context + useReducer (built-in)

    • সহজ অ্যাপ/মধ্যবিত্ত কাজে ভাল। কিন্তু বড় state/অতিরিক্ত frequent updates হলে performance সমস্যা হতে পারে কারণ context value change হলে সব subscriber re-render হতে পারে (memoize ও splitting দরকার)।

  • Zustand

    • API সহজ ও হালকা। store outside React; components শুধু selective slice subscribe করে — re-render কমায়।

    • উদাহরণ:

// store.js import create from "zustand"; export const useStore = create(set => ({ count: 0, inc: () => set(state => ({ count: state.count + 1 })), reset: () => set({ count: 0 }), })); // Component function Counter() { const { count, inc } = useStore(state => ({ count: state.count, inc: state.inc })); return <div>{count} <button onClick={inc}>+1</button></div>; }
  • সুবিধা: small API, slices subscribe, middleware support (persist), minimal boilerplate।

  • Jotai

    • atom-based approach (fine-grained reactive atoms). প্রতিটি atom শুধু যেসব component use করে তাদেরই re-render করে — খুব modular।

    • উদাহরণ:

import { atom, useAtom } from "jotai"; const countAtom = atom(0); function Counter() { const [count, setCount] = useAtom(countAtom); return <div>{count} <button onClick={() => setCount(c => c+1)}>+1</button></div>; }
  • সুবিধা: simplicity, derived atoms, async atoms।

  • Redux (Toolkit)

    • বড় অ্যাপ যেখানে predictable state transitions, devtools, middleware লাগবে। Redux Toolkit (RTK) boilerplate অনেক কমিয়ে দেয়। কিন্তু learning curve ও boilerplate আছে (RTK ছাড়া কন্সিডার করবেন না)।

  • Recoil

    • atom/selectors ধারণা—Jotai যেমন। (ব্যবহার কমবেশি একই শ্রেণীর)।

কোনটা বেছে নেবেন?

  • ছোট/মিড অ্যাপ: Context + useReducer পর্যাপ্ত।

  • যদি ফাইন-গ্রেইন updates দরকার বা simple API চান: Zustand খুব ভালো।

  • যদি atom/derived reactive approach পছন্দ: Jotai

  • বড়, complex state + middleware/devtools: Redux Toolkit

Performance টিপস Global state-এ:

  • সাবস্ক্রাইব করুন শুধুমাত্র প্রয়োজনীয় slice-এ (zustand selector, jotai atoms)।

  • Context value জিনিসকে memoize করুন কনস্ট্যান্ট না হলে নতুন object প্রদান করবেন না।

  • Avoid storing large immutable objects that change often in global state — ক্যাশ/DB-synced approach consider করুন।

6) Context API — use cases, example, pitfalls

Context = global-ish dependency injection. Use case: theme, auth, locale, UI preferences.

Basic pattern:

// AuthContext.js import React, { createContext, useContext, useState } from "react"; const AuthContext = createContext(); export function AuthProvider({ children }) { const [user, setUser] = useState(null); const login = (u) => setUser(u); const logout = () => setUser(null); const value = { user, login, logout }; return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; } export const useAuth = () => useContext(AuthContext);

ব্যবহার:

// index.jsx ReactDOM.render( <AuthProvider> <App /> </AuthProvider>, document.getElementById("root") ); // any component function Profile() { const { user, logout } = useAuth(); if (!user) return <div>Please login</div>; return <div>{user.name} <button onClick={logout}>Logout</button></div>; }

Pitfalls:

  • Large context value changes cause wide re-renders: যখন Provider value re-create হয় (e.g., {user, login, logout} প্রবতমান), তখন সব consumer re-render হবে। সমাধান:

    • split context (authContext, themeContext আলাদা করুন)

    • value memoize করুন: const value = useMemo(()=>({user, login, logout}), [user]) — তবে functions স্থিতিশীল রাখার জন্য useCallback দরকার হলে ব্যবহার করুন।

  • Overuse: সব state context-এ রাখবেন না। শুধুই cross-cutting concerns রাখবেন।

  • Testing: wrapper provider দিয়ে test করুন, বা mock করার সহজ উপায় রাখুন।

কিছু বাস্তব-জীবনের টিপস ও patterns (প্র্যাক্টিক্যাল)

  1. Start local, lift to global only when needed

    • প্রথমে component local state ব্যবহার করুন; যদি props drilling বেশি হয়, তখন context বা zustand বিবেচনা করুন।

  2. Keep hooks pure and small

    • custom hooks single responsibility রাখুন (e.g., useAuth শুধু auth logic, useUserProfile শুধু profile fetch করে)। এতে reuse সহজ হয়।

  3. Avoid premature optimization

    • profile করে bottleneck খুঁজুন। useMemo/useCallback/React.memo তখনই ব্যবহার করুন যখন profiling দেখায় পুনরাবৃত্তি বেশি বা expensive render আছে।

  4. Stable identities for handlers

    • parent থেকে child-এ callback পাঠাতে চাইলে useCallback ব্যবহার করুন। না হলে memoized child বারবার re-render পাবে।

  5. Error boundaries

    • component tree crash রোধ করতে Error Boundary ব্যবহার করুন (class component দিয়ে)।

  6. TypeScript (optional but recommended)

    • props ও state types না থাকলে বড় প্রজেক্টে bug বেশি হয়। TS use করলে hooks ও context safer হয়।

ছোট চেকলিস্ট (তৎক্ষণাৎ প্রয়োগযোগ্য)

  • যদি component বারবার re-render করে, React Profiler চালান।

  • ছোট components রাখুন, single responsibility বজায় রাখুন।

  • heavy computation হলে useMemo ব্যাবহার করুন (profiling দিয়ে validate করুন)।

  • functions props পাঠালে useCallback বিবেচনা করুন।

  • global state লাগলে প্রথমে context -> তারপর zustand/jotai/RTK evaluate করুন।

  • effect-এ async work করলে AbortController বা mounted flag ব্যবহার করুন।


আমি এটাকে practical রাখতে কিছু সংক্ষিপ্ত উদাহরণ যোগ করলাম (একসাথে বড় উদাহরণ চাইলে আমি পূর্ণ mini-project দেব):

  1. Custom hook + context combo (Auth + useAuthFetch):

// authStore.js (zustand example) import create from "zustand"; export const useAuthStore = create(set => ({ user: null, login: (user) => set({ user }), logout: () => set({ user: null }), })); // useAuthFetch.js import { useEffect, useState } from "react"; import { useAuthStore } from "./authStore"; export function useAuthFetch(url) { const { user } = useAuthStore(); const [data, setData] = useState(null); useEffect(() => { if (!user) return; fetch(url, { headers: { Authorization: `Bearer ${user.token}` } }) .then(r => r.json()) .then(setData) .catch(console.error); }, [url, user?.token]); return data; }
  1. Avoiding unnecessary re-renders with memo & callback:

const TodoItem = React.memo(({ todo, onToggle }) => { console.log("render", todo.id); return <div onClick={() => onToggle(todo.id)}>{todo.text}</div>; }); function TodoList({ todos }) { const [items, setItems] = useState(todos); const toggle = useCallback(id => { setItems(prev => prev.map(it => it.id === id ? {...it, done: !it.done} : it)); }, []); return items.map(t => <TodoItem key={t.id} todo={t} onToggle={toggle} />); }

No comments

Theme images by fpm. Powered by Blogger.