dev.rean.me
⭐ FEATURED POSTreact-js

Complete ReactJS References & Tips with Code Examples

Comprehensive ReactJS reference guide covering components, props, state, hooks, routing, events, patterns, and practical tips with code examples and related tutorials.

2026-09-19 · 25 min read

Share:

Hello my friend! Welcome to the Complete ReactJS References & Tips guide on dev.rean.me!

React is the most popular JavaScript library for building fast, interactive, and reusable user interfaces. In this guide, we cover all essential React concepts — components, props, state, hooks, events, routing, patterns and best practices — all grouped by category with clear code examples and related tutorial links. Bookmark this page for quick daily reference! ⚛️


1. Project Setup & File Structure

# Create new React + Vite project
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run dev
my-app/
├── public/             # Static assets (favicon, images)
├── src/
│   ├── assets/         # Images, fonts imported in code
│   ├── components/     # Reusable shared UI components
│   │   └── Navbar.jsx
│   ├── pages/          # Page-level components
│   │   ├── Home.jsx
│   │   └── About.jsx
│   ├── hooks/          # Custom hooks
│   │   └── useFetch.js
│   ├── context/        # React Context providers
│   ├── utils/          # Utility/helper functions
│   ├── App.jsx         # Root component with routing
│   └── main.jsx        # Entry point — renders App into DOM
├── index.html
├── package.json
└── vite.config.js

💡 Tip: Keep components/ for small reusable pieces (Button, Card, Modal) and pages/ for full page views (HomePage, AboutPage). This separation makes large projects much easier to navigate!


2. JSX Syntax Rules

JSX is the HTML-like syntax used inside React components. It compiles to React.createElement() calls.

RuleCorrectWrong
Single root element<> ... </> or <div>Two sibling elements
Self-closing tags<img /> <input /><img> <input>
Class attributeclassName=""class=""
Inline stylesstyle={{ color: "red" }}style="color: red"
JS expressions{variable} {fn()}Direct JS without {}
Event namesonClick onChangeonclick onchange
HTML entities&copy; &amp;Direct special chars
Comments{/* comment */}// comment inside JSX
function UserCard({ name, age, avatarUrl, isAdmin }) {
  const fullTitle = isAdmin ? "Admin" : "Member";

  return (
    // Must have single root — use Fragment <> to avoid extra div!
    <>
      <img
        src={avatarUrl}
        alt={`Avatar of ${name}`}
        className="avatar"
        style={{ borderRadius: "50%", width: 80 }}
      />
      <h2 className="card-title">{name}</h2>
      <p>Age: {age}</p>
      <p>Role: {fullTitle}</p>
      {/* Conditional rendering */}
      {isAdmin && <span className="badge">⭐ Admin</span>}
    </>
  );
}

💡 Tip: Use <>...</> (Fragment shorthand) as your root element instead of <div> to avoid unnecessary DOM wrapper nodes that can break Flexbox and Grid layouts!


3. Components & Props

Component Types

TypeSyntaxUse When
Functional (Modern)function MyComp() {}Always — standard in React 16.8+
Arrow Functionalconst MyComp = () => {}Same as above, concise syntax
Class (Legacy)class MyComp extends ComponentAvoid — only in old codebases
// ── FUNCTIONAL COMPONENT (standard) ──────────────
function Welcome({ name, role = "Member" }) {
  return (
    <div className="welcome-card">
      <h1>Hello, {name}!</h1>
      <p>Role: {role}</p>
    </div>
  );
}

// ── ARROW FUNCTION COMPONENT ──────────────────────
const WelcomeArrow = ({ name }) => (
  <h1>Welcome, {name}!</h1>
);

// ── PASSING PROPS (Parent → Child) ───────────────
function App() {
  return (
    <div>
      <Welcome name="Sok Dara" role="Admin" />
      <Welcome name="Keo Bopha" />  {/* role defaults to "Member" */}
    </div>
  );
}

export default App;

Props Patterns

// ── CHILDREN PROP ─────────────────────────────────
function Card({ title, children }) {
  return (
    <div className="card">
      <h3>{title}</h3>
      <div className="card-body">{children}</div>
    </div>
  );
}

// Usage:
<Card title="My Card">
  <p>This content is passed as children!</p>
  <button>Click Me</button>
</Card>

// ── SPREAD PROPS (pass all props at once) ─────────
const buttonProps = { type: "submit", disabled: false, className: "btn-primary" };
<button {...buttonProps}>Submit</button>

// ── PROP TYPES PATTERN (runtime type checking) ────
// Install: npm install prop-types
import PropTypes from "prop-types";

Welcome.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number,
  isAdmin: PropTypes.bool,
  onLogin: PropTypes.func,
  tags: PropTypes.arrayOf(PropTypes.string),
};
Welcome.defaultProps = {
  age: 0,
  isAdmin: false,
};

💡 Tip: Always destructure props directly in the function parameter ({ name, age }) instead of using props.name — it's cleaner and documents what the component expects at a glance!


4. State Management — useState

State is data stored inside a component that, when changed, causes the component to re-render.

import { useState } from "react";

// ── BASIC COUNTER ─────────────────────────────────
function Counter() {
  const [count, setCount] = useState(0); // initial value = 0

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(count - 1)}>-1</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}

// ── FUNCTIONAL UPDATER (use when new state depends on previous) ──
function SafeCounter() {
  const [count, setCount] = useState(0);

  // ✅ Safe! Always uses latest count value
  const increment = () => setCount(prev => prev + 1);

  return <button onClick={increment}>Count: {count}</button>;
}

// ── OBJECT STATE ──────────────────────────────────
function UserProfile() {
  const [user, setUser] = useState({ name: "Rean", age: 25 });

  // ✅ Spread existing state to avoid overwriting other fields!
  const updateName = (newName) =>
    setUser(prev => ({ ...prev, name: newName }));

  return (
    <div>
      <p>Name: {user.name} | Age: {user.age}</p>
      <input onChange={(e) => updateName(e.target.value)} />
    </div>
  );
}

// ── ARRAY STATE ───────────────────────────────────
function TodoList() {
  const [todos, setTodos] = useState(["Learn React", "Build App"]);

  // Add item
  const addTodo = (text) =>
    setTodos(prev => [...prev, text]);

  // Remove item by index
  const removeTodo = (index) =>
    setTodos(prev => prev.filter((_, i) => i !== index));

  // Update item by index
  const updateTodo = (index, newText) =>
    setTodos(prev =>
      prev.map((item, i) => (i === index ? newText : item))
    );

  return (
    <ul>
      {todos.map((todo, i) => (
        <li key={i}>
          {todo}
          <button onClick={() => removeTodo(i)}>✕</button>
        </li>
      ))}
    </ul>
  );
}

💡 Tip: NEVER mutate state directly like user.name = "Dara" or todos.push("item")! React detects changes by comparing old and new state references — direct mutation doesn't trigger a re-render!


5. Event Handling

React event names are camelCase and receive a synthetic event object (e).

Common Event Handlers

EventHandler PropUse Case
Mouse clickonClickButtons, links, cards
Input changeonChangeText inputs, selects, checkboxes
Form submitonSubmitForm submission
Focus / BluronFocus / onBlurInput validation
Key pressonKeyDown / onKeyUpShortcuts, Enter key
Mouse hoveronMouseEnter / onMouseLeaveTooltips, hover effects
Drag eventsonDragStart / onDropDrag & drop UI
import { useState } from "react";

function EventExamples() {
  const [text, setText] = useState("");
  const [submitted, setSubmitted] = useState(null);

  // ✅ Pass function reference — NOT invocation!
  const handleClick = () => alert("Button clicked!");

  // ✅ Pass args using arrow function wrapper
  const handleGreet = (name) => alert(`Hello, ${name}!`);

  // ✅ onChange — read e.target.value
  const handleChange = (e) => setText(e.target.value);

  // ✅ Always call e.preventDefault() on form submit!
  const handleSubmit = (e) => {
    e.preventDefault();
    setSubmitted(text);
    setText(""); // clear input after submit
  };

  // ✅ Keyboard shortcut detection
  const handleKeyDown = (e) => {
    if (e.key === "Enter") handleSubmit(e);
    if (e.key === "Escape") setText("");
  };

  return (
    <div>
      {/* ❌ Wrong: handleClick() — runs immediately on render! */}
      {/* ✅ Correct: handleClick — reference only */}
      <button onClick={handleClick}>Click Me</button>
      <button onClick={() => handleGreet("Dara")}>Greet Dara</button>

      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={text}
          onChange={handleChange}
          onKeyDown={handleKeyDown}
          placeholder="Type something..."
        />
        <button type="submit">Submit</button>
      </form>

      {submitted && <p>✅ Submitted: {submitted}</p>}
    </div>
  );
}

💡 Tip: Never write onClick={handleClick()} — the () invokes the function immediately during render, not on click. Write onClick={handleClick} (reference) or onClick={() => handleClick(args)} (with arguments).


6. React Hooks — Complete Reference

Hooks let functional components use state, lifecycle, context, and more.

Rules of Hooks

  1. ✅ Only call hooks at the top level — not inside loops, conditions, or nested functions
  2. ✅ Only call hooks inside React functional components (or custom hooks)

Built-in Hooks Quick Reference

HookPurpose
useStateStore and update local component state
useEffectRun side effects (fetch, timers, subscriptions)
useContextConsume a React Context value
useRefMutable ref without re-render; access DOM nodes
useMemoMemoize expensive computed values
useCallbackMemoize function references (stable between renders)
useReducerManage complex state with a reducer function
useIdGenerate unique IDs for accessibility
useTransitionMark non-urgent state updates for concurrent mode
useDeferredValueDefer updating a value for performance
import { useState, useEffect, useRef, useMemo, useCallback, useReducer } from "react";

// ── useEffect ─────────────────────────────────────
function UserFetcher({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Runs after every render where userId changes
    setLoading(true);

    const controller = new AbortController();

    fetch(`/api/users/${userId}`, { signal: controller.signal })
      .then(r => r.json())
      .then(data => {
        setUser(data);
        setLoading(false);
      })
      .catch(err => {
        if (err.name !== "AbortError") console.error(err);
      });

    // Cleanup: cancel fetch if component unmounts or userId changes
    return () => controller.abort();
  }, [userId]); // Dependency array — re-run when userId changes

  if (loading) return <p>Loading...</p>;
  return <p>User: {user?.name}</p>;
}

// ── useRef ────────────────────────────────────────
function FocusInput() {
  const inputRef = useRef(null);

  // Focus the input programmatically
  const focusInput = () => inputRef.current?.focus();

  return (
    <>
      <input ref={inputRef} type="text" placeholder="Click button to focus" />
      <button onClick={focusInput}>Focus Input</button>
    </>
  );
}

// ── useMemo (memoize expensive computation) ───────
function ExpensiveList({ items, filter }) {
  const filteredItems = useMemo(
    () => items.filter(item => item.name.includes(filter)),
    [items, filter] // Only recomputes when items or filter changes
  );

  return <ul>{filteredItems.map(i => <li key={i.id}>{i.name}</li>)}</ul>;
}

// ── useCallback (stable function reference) ───────
function Parent() {
  const [count, setCount] = useState(0);

  // Without useCallback, new function reference on every render
  // causes Child to re-render unnecessarily
  const handleAdd = useCallback(() => {
    setCount(prev => prev + 1);
  }, []); // Never changes — empty deps

  return <Child onAdd={handleAdd} />;
}

// ── useReducer (complex state logic) ─────────────
const initialState = { count: 0, step: 1 };

function reducer(state, action) {
  switch (action.type) {
    case "increment": return { ...state, count: state.count + state.step };
    case "decrement": return { ...state, count: state.count - state.step };
    case "setStep":   return { ...state, step: action.payload };
    case "reset":     return initialState;
    default: throw new Error(`Unknown action: ${action.type}`);
  }
}

function AdvancedCounter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count} | Step: {state.step}</p>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
      <button onClick={() => dispatch({ type: "setStep", payload: 5 })}>Set Step 5</button>
      <button onClick={() => dispatch({ type: "reset" })}>Reset</button>
    </div>
  );
}

💡 Tip: Use useReducer instead of multiple useState hooks when your state has many fields that update together, or when the next state depends on multiple parts of current state. It makes the logic much easier to test!


7. useEffect — Side Effects Reference

useEffect runs after the browser has painted the screen.

Dependency ArrayRuns When
No array: useEffect(fn)After every render
Empty array: useEffect(fn, [])Once — on mount only (like componentDidMount)
With deps: useEffect(fn, [a, b])On mount + whenever a or b changes
Return cleanup functionCleanup runs on unmount or before next effect
import { useState, useEffect } from "react";

// ── Common useEffect Patterns ─────────────────────

// 1. Fetch on mount
useEffect(() => {
  fetch("/api/data").then(r => r.json()).then(setData);
}, []); // [] = only once on mount

// 2. React to prop/state changes
useEffect(() => {
  document.title = `Count: ${count}`;
}, [count]); // Re-run when count changes

// 3. Cleanup (timers, subscriptions, fetch abort)
useEffect(() => {
  const intervalId = setInterval(() => {
    setTick(t => t + 1);
  }, 1000);

  return () => clearInterval(intervalId); // Cleanup on unmount!
}, []);

// 4. Event listener on window
useEffect(() => {
  const handleResize = () => setWidth(window.innerWidth);
  window.addEventListener("resize", handleResize);
  return () => window.removeEventListener("resize", handleResize);
}, []);

// 5. Local storage sync
useEffect(() => {
  localStorage.setItem("theme", theme);
}, [theme]);

💡 Tip: Always return a cleanup function inside useEffect when you create subscriptions, timers, or fetch requests! Forgetting cleanup is the most common cause of memory leaks in React apps.


8. Context API — Global State

React Context lets you share data across your component tree without passing props at every level (avoids "prop drilling").

import { createContext, useContext, useState } from "react";

// ── 1. Create Context ─────────────────────────────
const ThemeContext = createContext("light"); // default value

// ── 2. Create Provider Component ──────────────────
export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");

  const toggleTheme = () =>
    setTheme(prev => (prev === "light" ? "dark" : "light"));

  return (
    // All children now have access to theme & toggleTheme
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// ── 3. Custom Hook (clean consumer pattern) ───────
export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error("useTheme must be used inside ThemeProvider!");
  }
  return context;
}

// ── 4. Use in any child component ─────────────────
function Header() {
  const { theme, toggleTheme } = useTheme();

  return (
    <header className={`header header--${theme}`}>
      <h1>My App</h1>
      <button onClick={toggleTheme}>
        Switch to {theme === "light" ? "Dark" : "Light"} Mode
      </button>
    </header>
  );
}

// ── 5. Wrap your app ──────────────────────────────
function App() {
  return (
    <ThemeProvider>
      <Header />
      {/* All nested components can access theme! */}
    </ThemeProvider>
  );
}

💡 Tip: Always create a custom hook like useTheme() or useAuth() to consume context! This pattern adds error checking (when context is used outside its provider) and hides useContext implementation details from consumers.


9. Custom Hooks

Custom hooks extract reusable stateful logic into standalone functions. They always start with use.

import { useState, useEffect, useCallback } from "react";

// ── useFetch: reusable data fetching ──────────────
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!url) return;
    const controller = new AbortController();
    setLoading(true);
    setError(null);

    fetch(url, { signal: controller.signal })
      .then(res => {
        if (!res.ok) throw new Error(`HTTP Error ${res.status}`);
        return res.json();
      })
      .then(setData)
      .catch(err => {
        if (err.name !== "AbortError") setError(err.message);
      })
      .finally(() => setLoading(false));

    return () => controller.abort();
  }, [url]);

  return { data, loading, error };
}

// ── useLocalStorage: persist state to localStorage ──
function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    try {
      return JSON.parse(localStorage.getItem(key)) ?? initialValue;
    } catch {
      return initialValue;
    }
  });

  const setStoredValue = useCallback((newValue) => {
    setValue(newValue);
    localStorage.setItem(key, JSON.stringify(newValue));
  }, [key]);

  return [value, setStoredValue];
}

// ── useDebounce: debounce rapidly changing values ──
function useDebounce(value, delay = 300) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebouncedValue(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debouncedValue;
}

// ── useToggle: boolean toggle helper ──────────────
function useToggle(initial = false) {
  const [value, setValue] = useState(initial);
  const toggle = useCallback(() => setValue(v => !v), []);
  return [value, toggle];
}

// ── Usage Examples ─────────────────────────────────
function UsersPage() {
  const { data: users, loading, error } = useFetch("/api/users");
  const [savedUsers, setSavedUsers] = useLocalStorage("users", []);
  const [isMenuOpen, toggleMenu] = useToggle(false);

  const [search, setSearch] = useState("");
  const debouncedSearch = useDebounce(search, 400);

  // Only fetches when debouncedSearch stabilizes
  const { data: results } = useFetch(
    debouncedSearch ? `/api/search?q=${debouncedSearch}` : null
  );

  if (loading) return <p>Loading users...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <button onClick={toggleMenu}>{isMenuOpen ? "Close" : "Open"} Menu</button>
      <input value={search} onChange={e => setSearch(e.target.value)} />
      <ul>{users?.map(u => <li key={u.id}>{u.name}</li>)}</ul>
    </div>
  );
}

💡 Tip: Custom hooks are just JavaScript functions — they can call other custom hooks, can be tested in isolation, and can be shared across projects as npm packages. Always name them starting with use so React linting rules apply!


10. Conditional Rendering

function UserDashboard({ user, isLoading, isError }) {
  // ── Pattern 1: Early return (guard clause) ────────
  if (isLoading) return <p>Loading...</p>;
  if (isError)   return <p>Something went wrong!</p>;
  if (!user)     return <p>No user found.</p>;

  return (
    <div>
      {/* ── Pattern 2: && short-circuit ──────────── */}
      {user.isAdmin && <span className="badge">Admin</span>}

      {/* ── Pattern 3: Ternary ───────────────────── */}
      <p>Status: {user.isActive ? "🟢 Active" : "🔴 Inactive"}</p>

      {/* ── Pattern 4: Variable ──────────────────── */}
      {(() => {
        if (user.role === "admin") return <AdminPanel />;
        if (user.role === "editor") return <EditorPanel />;
        return <ViewerPanel />;
      })()}

      {/* ── Pattern 5: Nullish coalescing ────────── */}
      <p>{user.bio ?? "No bio provided."}</p>
    </div>
  );
}

💡 Tip: Avoid && with numbers! {count && <Component />} renders 0 when count is 0 instead of nothing. Use {count > 0 && <Component />} or {Boolean(count) && <Component />} instead!


11. List Rendering & Keys

const users = [
  { id: 1, name: "Sok Dara", role: "Dev" },
  { id: 2, name: "Keo Bopha", role: "Designer" },
  { id: 3, name: "Vibol Kiri", role: "PM" },
];

function UserList() {
  return (
    <ul>
      {users.map(user => (
        // ✅ key must be unique and stable — use ID, not index!
        <li key={user.id}>
          <strong>{user.name}</strong> — {user.role}
        </li>
      ))}
    </ul>
  );
}

// ── Fragment with key (when wrapping multiple elements) ──
function ProductRows({ products }) {
  return (
    <tbody>
      {products.map(product => (
        // ✅ Use React.Fragment with key when returning multiple elements
        <React.Fragment key={product.id}>
          <tr><td>{product.name}</td></tr>
          <tr><td>{product.description}</td></tr>
        </React.Fragment>
      ))}
    </tbody>
  );
}

// ── Filtering + Sorting before render ────────────
function FilteredList({ items, searchTerm }) {
  const displayed = items
    .filter(item => item.name.toLowerCase().includes(searchTerm.toLowerCase()))
    .sort((a, b) => a.name.localeCompare(b.name));

  if (displayed.length === 0) return <p>No results found.</p>;

  return (
    <ul>
      {displayed.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

💡 Tip: Never use array index as key when list items can be reordered, filtered, or deleted — React uses keys to track identity. Using index causes state, animations, and focus to misbehave! Always use a stable unique ID from your data.


12. Forms — Controlled & Uncontrolled

import { useState, useRef } from "react";

// ── CONTROLLED FORM (React manages value) ─────────
function ControlledForm() {
  const [form, setForm] = useState({
    name: "",
    email: "",
    role: "member",
    agree: false,
  });

  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    setForm(prev => ({
      ...prev,
      [name]: type === "checkbox" ? checked : value,
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log("Form submitted:", form);
  };

  const isValid = form.name.trim() && form.email.includes("@") && form.agree;

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="name"
        value={form.name}
        onChange={handleChange}
        placeholder="Full Name"
        required
      />
      <input
        name="email"
        type="email"
        value={form.email}
        onChange={handleChange}
        placeholder="Email"
      />
      <select name="role" value={form.role} onChange={handleChange}>
        <option value="member">Member</option>
        <option value="editor">Editor</option>
        <option value="admin">Admin</option>
      </select>
      <label>
        <input
          name="agree"
          type="checkbox"
          checked={form.agree}
          onChange={handleChange}
        />
        I agree to terms
      </label>
      <button type="submit" disabled={!isValid}>Register</button>
    </form>
  );
}

// ── UNCONTROLLED FORM (DOM manages value) ─────────
function UncontrolledForm() {
  const nameRef = useRef(null);
  const emailRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({
      name: nameRef.current.value,
      email: emailRef.current.value,
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input ref={nameRef} name="name" placeholder="Full Name" />
      <input ref={emailRef} name="email" type="email" placeholder="Email" />
      <button type="submit">Submit</button>
    </form>
  );
}

💡 Tip: Use the [name] computed property key pattern in handleChange to handle ALL input fields with a single handler function instead of writing separate setName, setEmail, setRole handlers!


13. React Router — Navigation Reference

npm install react-router-dom

Core Components

ComponentPurpose
<BrowserRouter>Wraps entire app — enables client-side routing
<Routes>Container for all <Route> definitions
<Route path="" element={}>Maps URL path to a component
<Link to="">Navigation link (no page reload)
<NavLink to="">Link with active class support
<Navigate to="">Programmatic redirect inside JSX
<Outlet>Renders nested child routes

Hooks

HookReturnsUse Case
useNavigate()navigate functionProgrammatic navigation
useParams()URL params objectRead :id from URL
useSearchParams()[params, setParams]Read/write query strings
useLocation()Location objectCurrent URL, state
import {
  BrowserRouter, Routes, Route, Link, NavLink,
  Navigate, Outlet, useNavigate, useParams
} from "react-router-dom";

// ── App Router Setup ──────────────────────────────
function App() {
  return (
    <BrowserRouter>
      <Navbar />
      <Routes>
        <Route path="/"        element={<HomePage />} />
        <Route path="/about"   element={<AboutPage />} />

        {/* Dynamic route with param */}
        <Route path="/users/:id" element={<UserDetailPage />} />

        {/* Nested routes */}
        <Route path="/dashboard" element={<DashboardLayout />}>
          <Route index         element={<DashboardHome />} />
          <Route path="stats"  element={<StatsPage />} />
          <Route path="settings" element={<SettingsPage />} />
        </Route>

        {/* Redirect */}
        <Route path="/old-page" element={<Navigate to="/new-page" replace />} />

        {/* 404 catch-all */}
        <Route path="*" element={<NotFoundPage />} />
      </Routes>
    </BrowserRouter>
  );
}

// ── Navbar with NavLink ───────────────────────────
function Navbar() {
  return (
    <nav>
      {/* NavLink automatically adds "active" class when route matches */}
      <NavLink to="/" end>Home</NavLink>
      <NavLink to="/about">About</NavLink>
      <NavLink to="/dashboard">Dashboard</NavLink>
    </nav>
  );
}

// ── Dynamic route — read URL params ───────────────
function UserDetailPage() {
  const { id } = useParams(); // /users/42 → id = "42"
  const { data: user, loading } = useFetch(`/api/users/${id}`);

  if (loading) return <p>Loading...</p>;
  return <h1>User: {user?.name}</h1>;
}

// ── Nested layout — render children in <Outlet> ───
function DashboardLayout() {
  return (
    <div className="dashboard">
      <aside>
        <Link to="/dashboard">Home</Link>
        <Link to="/dashboard/stats">Stats</Link>
        <Link to="/dashboard/settings">Settings</Link>
      </aside>
      <main>
        <Outlet /> {/* Child routes render here */}
      </main>
    </div>
  );
}

// ── Programmatic navigation ────────────────────────
function LoginPage() {
  const navigate = useNavigate();

  const handleLogin = async () => {
    await loginUser();
    navigate("/dashboard", { replace: true }); // replace history entry
  };

  return <button onClick={handleLogin}>Login</button>;
}

💡 Tip: Never use <a href=""> for internal links in React! It causes a full page reload and resets all React state. Always use <Link to=""> from react-router-dom for internal navigation!


14. Performance Optimization

import { memo, useMemo, useCallback, lazy, Suspense } from "react";

// ── React.memo (skip re-render if props unchanged) ──
const UserCard = memo(function UserCard({ name, role }) {
  console.log("Rendering UserCard:", name);
  return <div><h3>{name}</h3><p>{role}</p></div>;
});

// ── Lazy loading components (code splitting) ───────
const HeavyChart = lazy(() => import("./HeavyChart"));
const AdminPanel = lazy(() => import("./AdminPanel"));

function Dashboard({ isAdmin }) {
  return (
    <Suspense fallback={<div>Loading component...</div>}>
      <HeavyChart />
      {isAdmin && <AdminPanel />}
    </Suspense>
  );
}

// ── useMemo — memoize computed value ──────────────
function ProductList({ products, discount }) {
  // Only recalculates when products or discount changes
  const discountedProducts = useMemo(() =>
    products.map(p => ({
      ...p,
      finalPrice: p.price * (1 - discount / 100),
    })),
    [products, discount]
  );

  return <ul>{discountedProducts.map(p => (
    <li key={p.id}>{p.name}: ${p.finalPrice.toFixed(2)}</li>
  ))}</ul>;
}

// ── useCallback — stable function reference ────────
function Parent() {
  const [count, setCount] = useState(0);
  const [items, setItems] = useState([]);

  // Without useCallback, new reference on every render → Child re-renders!
  const addItem = useCallback((item) => {
    setItems(prev => [...prev, item]);
  }, []); // Stable reference — never changes

  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>Re-render Parent ({count})</button>
      <MemoizedChildList onAdd={addItem} />
    </>
  );
}

💡 Tip: Don't over-optimize! Apply memo, useMemo, useCallback only when you measure a real performance problem with React DevTools Profiler. Premature optimization adds complexity without benefit!


15. Component Patterns

// ── COMPOUND COMPONENTS ───────────────────────────
function Tabs({ children }) {
  const [active, setActive] = useState(0);
  return (
    <div className="tabs">
      <div className="tab-list">
        {React.Children.map(children, (child, i) =>
          React.cloneElement(child, {
            isActive: i === active,
            onClick: () => setActive(i),
          })
        )}
      </div>
    </div>
  );
}

// ── RENDER PROPS ──────────────────────────────────
function MouseTracker({ render }) {
  const [pos, setPos] = useState({ x: 0, y: 0 });

  return (
    <div onMouseMove={e => setPos({ x: e.clientX, y: e.clientY })}>
      {render(pos)} {/* Caller controls what to render */}
    </div>
  );
}

// Usage: <MouseTracker render={({ x, y }) => <p>X:{x} Y:{y}</p>} />

// ── HIGHER-ORDER COMPONENT (HOC) ──────────────────
function withAuth(WrappedComponent) {
  return function AuthGuard(props) {
    const isLoggedIn = useAuth().isLoggedIn;
    if (!isLoggedIn) return <Navigate to="/login" />;
    return <WrappedComponent {...props} />;
  };
}

const ProtectedDashboard = withAuth(Dashboard);

// ── CONTROLLED vs UNCONTROLLED COMPONENTS ─────────
// Controlled: parent owns state via value + onChange
function ControlledInput({ value, onChange }) {
  return <input value={value} onChange={e => onChange(e.target.value)} />;
}

// Uncontrolled: internal state, exposed via ref/callback
function UncontrolledInput({ defaultValue, onBlurSubmit }) {
  const [val, setVal] = useState(defaultValue);
  return (
    <input
      value={val}
      onChange={e => setVal(e.target.value)}
      onBlur={() => onBlurSubmit(val)}
    />
  );
}

16. React Best Practices Summary

✅ Do❌ Avoid
Use Functional Components with HooksUsing Class Components (legacy)
Use <>...</> Fragment as root wrapperWrapping with unnecessary <div>
Use key={item.id} stable unique IDUsing key={index} in lists
Use functional updater setCount(prev => prev + 1)Direct setCount(count + 1) in async
Call e.preventDefault() on form submitLetting forms reload the page
Use <Link to=""> for internal navigationUsing <a href=""> inside React Router apps
Use useCallback for props passed to memoized childrenRecreating functions inline in render
Keep components small & single-purposeGiant 500+ line components
Use Context or state management for global dataDeep prop drilling (5+ levels)
Lift state up to the nearest common ancestorDuplicating state in sibling components
Return cleanup in useEffectForgetting to cancel subscriptions/timers
Use structuredClone() for deep copyJSON.parse(JSON.stringify())
TypeScript for type safetyany type everywhere in TypeScript

Summary Tag Cloud Directory

Happy coding and building awesome React applications! ⚛️🚀

← Back to react-js
Share: