React Mastery

Category: Coding & Engineering | Read: 9 min | v1.0.0

React Mastery

Overview

Comprehensive reference for advanced React development: hooks internals, state management architectures, component patterns, security hardening, and performance + testing strategies. Use this skill when building production-grade React apps, debugging complex rendering, or architecting scalable frontend systems.

1. Hooks Deep Dive

Rules of Hooks

  • Only call hooks at the top level — never inside loops, conditions, or nested functions.
  • Only call hooks from React function components or custom hooks.
  • React relies on call order to associate state with hook slots; violating order breaks internal linked-list tracking.
  • // ❌ WRONG — conditional hook
    if (isLoggedIn) {
      const [user, setUser] = useState(null);
    }
    

    // ✅ CORRECT — always call, conditionally use
    const [user, setUser] = useState(null);
    if (!isLoggedIn) return null;

    useEffect Lifecycle Patterns

    // Mount only
    useEffect(() => { initAnalytics(); }, []);
    

    // Sync with dependency
    useEffect(() => { document.title = Count: ${count}; }, [count]);

    // Cleanup (unmount + re-run)
    useEffect(() => {
    const controller = new AbortController();
    fetch(/api/data, { signal: controller.signal })
    .then(r => r.json()).then(setData);
    return () => controller.abort();
    }, [query]);

    // Subscription pattern
    useEffect(() => {
    const sub = eventBus.subscribe('refresh', handleRefresh);
    return () => sub.unsubscribe();
    }, [handleRefresh]);

    useReducer for Complex State

    const initialState = { items: [], loading: false, error: null };
    

    function reducer(state, action) {
    switch (action.type) {
    case 'FETCH_START': return { ...state, loading: true, error: null };
    case 'FETCH_SUCCESS': return { ...state, loading: false, items: action.payload };
    case 'FETCH_ERROR': return { ...state, loading: false, error: action.payload };
    case 'ADD_ITEM': return { ...state, items: [...state.items, action.payload] };
    case 'REMOVE_ITEM': return { ...state, items: state.items.filter(i => i.id !== action.payload) };
    default: return state;
    }
    }

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

    useMemo & useCallback

    // Memoize expensive computation
    const sortedItems = useMemo(
      () => [...items].sort((a, b) => a.priority - b.priority), [items]
    );
    

    // Memoize callback reference for child component
    const handleClick = useCallback((id) => { setSelected(id); }, []);

    Custom Hooks

    function useDebounce(value, delay = 300) {
      const [debounced, setDebounced] = useState(value);
      useEffect(() => {
        const timer = setTimeout(() => setDebounced(value), delay);
        return () => clearTimeout(timer);
      }, [value, delay]);
      return debounced;
    }
    

    function useAsync(asyncFn, deps = []) {
    const [state, dispatch] = useReducer(
    (s, a) => ({ ...s, ...a }), { data: null, loading: true, error: null }
    );
    useEffect(() => {
    let cancelled = false;
    dispatch({ loading: true, error: null });
    asyncFn()
    .then(data => { if (!cancelled) dispatch({ data, loading: false }); })
    .catch(err => { if (!cancelled) dispatch({ error: err, loading: false }); });
    return () => { cancelled = true; };
    }, deps);
    return state;
    }

    2. State Management

    Architecture Decision Flow

    graph TD
        A[State Need] --> B{Scope?}
        B -->|Component-local| C[useState / useReducer]
        B -->|Shared across subtree| D[Context API]
        B -->|Global + frequent updates| E{Complexity?}
        E -->|Medium| F[Zustand / Jotai]
        E -->|High + middleware| G[Redux Toolkit]
        A --> H{Server data?}
        H -->|Yes| I[TanStack Query]
        H -->|No| B
    

    Context API

    const ThemeCtx = createContext({ theme: 'light', toggle: () => {} });
    

    function ThemeProvider({ children }) {
    const [theme, setTheme] = useState('light');
    const toggle = useCallback(() => setTheme(t => t === 'light' ? 'dark' : 'light'), []);
    const value = useMemo(() => ({ theme, toggle }), [theme, toggle]);
    return {children};
    }

    Zustand — Minimal Global Store

    import { create } from 'zustand';
    import { devtools, persist } from 'zustand/middleware';
    

    const useStore = create(devtools(persist((set, get) => ({
    cart: [],
    addToCart: (item) => set(s => ({ cart: [...s.cart, item] })),
    removeFromCart: (id) => set(s => ({ cart: s.cart.filter(i => i.id !== id) })),
    total: () => get().cart.reduce((sum, i) => sum + i.price, 0),
    }), { name: 'cart-store' })));

    // Selector prevents unnecessary re-renders
    const items = useStore(s => s.cart);

    Jotai — Atomic State

    import { atom, useAtom, useAtomValue } from 'jotai';
    

    const userAtom = atom(null);
    const isLoggedInAtom = atom((get) => get(userAtom) !== null);

    function Profile() {
    const [user, setUser] = useAtom(userAtom);
    const loggedIn = useAtomValue(isLoggedInAtom);
    if (!loggedIn) return ;
    return

    {user.name}
    ;
    }

    Redux Toolkit

    import { createSlice, configureStore, createAsyncThunk } from '@reduxjs/toolkit';
    

    const fetchUsers = createAsyncThunk('users/fetch', async () => {
    const res = await fetch('/api/users'); return res.json();
    });

    const usersSlice = createSlice({
    name: 'users',
    initialState: { list: [], status: 'idle', error: null },
    reducers: { addUser: (s, a) => { s.list.push(a.payload); } },
    extraReducers: (b) => {
    b.addCase(fetchUsers.pending, s => { s.status = 'loading'; })
    .addCase(fetchUsers.fulfilled, (s, a) => { s.list = a.payload; s.status = 'succeeded'; })
    .addCase(fetchUsers.rejected, (s, a) => { s.status = 'failed'; s.error = a.error.message; });
    },
    });

    const store = configureStore({ reducer: { users: usersSlice.reducer } });

    TanStack Query — Server State

    import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
    

    function useUsers() {
    return useQuery({
    queryKey: ['users'],
    queryFn: () => fetch('/api/users').then(r => r.json()),
    staleTime: 5 * 60_000, retry: 2,
    });
    }

    function useAddUser() {
    const qc = useQueryClient();
    return useMutation({
    mutationFn: (u) => fetch('/api/users', { method: 'POST', body: JSON.stringify(u) }),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['users'] }),
    });
    }

    3. Component Patterns

    Component Hierarchy

    graph TD
        App --> ThemeProvider
        ThemeProvider --> Layout
        Layout --> Header
        Layout --> ErrorBoundary
        ErrorBoundary --> PageContent
        PageContent --> DataProvider
        DataProvider --> List
        List --> ListItem
        ListItem --> WithTooltip
    

    Compound Components

    function Tabs({ children, defaultIndex = 0 }) {
      const [activeIndex, setActiveIndex] = useState(defaultIndex);
      const ctx = useMemo(() => ({ activeIndex, setActiveIndex }), [activeIndex]);
      return {children};
    }
    

    Tabs.List = function TabList({ children }) {
    const { activeIndex, setActiveIndex } = useContext(TabsContext);
    return (


    {Children.map(children, (child, i) =>
    cloneElement(child, { isActive: i === activeIndex, onClick: () => setActiveIndex(i) })
    )}

    );
    };

    Tabs.Panel = function TabPanel({ children, index }) {
    const { activeIndex } = useContext(TabsContext);
    return activeIndex === index ?

    {children}
    : null;
    };

    Render Props

    function MouseTracker({ render }) {
      const [pos, setPos] = useState({ x: 0, y: 0 });
      useEffect(() => {
        const handler = (e) => setPos({ x: e.clientX, y: e.clientY });
        window.addEventListener('mousemove', handler);
        return () => window.removeEventListener('mousemove', handler);
      }, []);
      return render(pos);
    }
    // Usage:  } />
    

    Higher-Order Components (HOCs)

    function withAuth(WrappedComponent) {
      return function AuthenticatedComponent(props) {
        const { user, loading } = useAuth();
        if (loading) return ;
        if (!user) return ;
        return ;
      };
    }
    // Usage: export default withAuth(Dashboard);
    

    Controlled vs Uncontrolled

    // Controlled — React owns the truth
    const [value, setValue] = useState('');
     setValue(e.target.value)} />;
    

    // Uncontrolled — DOM owns the truth
    const ref = useRef();
    ;
    // Read: ref.current.value

    Error Boundaries

    class ErrorBoundary extends React.Component {
      state = { hasError: false, error: null };
      static getDerivedStateFromError(error) { return { hasError: true, error }; }
      componentDidCatch(error, info) { logErrorToService(error, info.componentStack); }
      render() {
        return this.state.hasError
          ? (this.props.fallback ?? 

    Something went wrong.

    ) : this.props.children; } }

    4. React Security

    XSS in JSX & dangerouslySetInnerHTML

    JSX auto-escapes {userInput}. The real risk is dangerouslySetInnerHTML and href injection:

    // ❌ VULNERABLE
    
    Click

    // ✅ SANITIZE
    import DOMPurify from 'dompurify';

    // ✅ VALIDATE PROTOCOL
    const SAFE_URL = /^(https?:|mailto:|tel:)/i;
    function SafeLink({ href, ...props }) {
    if (!SAFE_URL.test(href)) return Invalid Link;
    return ;
    }

    Prototype Pollution

    const DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
    function safeMerge(target, source) {
      const clean = Object.entries(source)
        .filter(([k]) => !DANGEROUS_KEYS.includes(k))
        .reduce((o, [k, v]) => ({ ...o, [k]: v }), {});
      return { ...target, ...clean };
    }
    

    Auth & Token Management

    function useAuth() {
      const [token, setToken] = useState(() => localStorage.getItem('token'));
      const login = async (creds) => {
        const { token } = await fetch('/api/login', {
          method: 'POST', headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(creds),
        }).then(r => r.json());
        localStorage.setItem('token', token);
        setToken(token);
      };
      const logout = () => { localStorage.removeItem('token'); setToken(null); };
      return { token, login, logout, isAuthenticated: !!token };
    }
    

    CSRF Protection

    // Axios interceptor for CSRF
    axios.interceptors.request.use((config) => {
      if (['post', 'put', 'patch', 'delete'].includes(config.method)) {
        config.headers['X-CSRF-Token'] = getCSRFToken();
      }
      return config;
    });
    

    5. Performance + Testing

    Render Cycle & Optimization

    sequenceDiagram
        participant Parent
        participant Child
        participant DOM
        Parent->>Parent: setState / dispatch
        Parent->>Child: Re-render (props change?)
        Note over Child: React.memo checks shallow equality
        alt Props unchanged
            Child-->>Parent: Skip render (memo hit)
        else Props changed
            Child->>Child: Render VDOM
            Child->>DOM: Commit minimal diff
        end
    

    React.memo & Optimization

    const ExpensiveList = React.memo(function ExpensiveList({ items, onSelect }) {
      return items.map(item => );
    }, (prev, next) => prev.items === next.items && prev.onSelect === next.onSelect);
    

    Code Splitting & Suspense

    const AdminPanel = React.lazy(() => import('./AdminPanel'));
    

    function App() {
    return (
    }>

    } />


    );
    }

    // Data fetching with Suspense
    function Profile() {
    const { data } = useSuspenseQuery({ queryKey: ['profile'], queryFn: fetchProfile });
    return

    {data.name}
    ;
    }

    Jest Unit Tests

    describe('formatCurrency', () => {
      it('formats USD', () => expect(formatCurrency(1234.5)).toBe('$1,234.50'));
      it('handles zero', () => expect(formatCurrency(0)).toBe('$0.00'));
      it('handles negatives', () => expect(formatCurrency(-10)).toBe('-$10.00'));
    });
    

    React Testing Library

    import { render, screen, waitFor } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    import LoginForm from './LoginForm';
    

    test('submits credentials and shows success', async () => {
    const onSuccess = jest.fn();
    render();
    const user = userEvent.setup();
    await user.type(screen.getByLabelText(/email/i), 'user@test.com');
    await user.type(screen.getByLabelText(/password/i), 'secret123');
    await user.click(screen.getByRole('button', { name: /sign in/i }));
    await waitFor(() => expect(onSuccess).toHaveBeenCalledWith({
    email: 'user@test.com', password: 'secret123',
    }));
    });

    Playwright E2E

    test('user can log in and see dashboard', async ({ page }) => {
      await page.goto('/login');
      await page.fill('[data-testid="email"]', 'admin@test.com');
      await page.fill('[data-testid="password"]', 'admin123');
      await page.click('button[type="submit"]');
      await expect(page).toHaveURL('/dashboard');
      await expect(page.locator('h1')).toContainText('Welcome, Admin');
    });
    

    Quick Reference Cheatsheet

    | Topic | Key API | When to Use |
    |-------|---------|-------------|
    | Local state | useState / useReducer | Component-scoped |
    | Shared subtree | Context API | Theme, locale, auth |
    | Global store | Zustand / Jotai | Medium-complexity global |
    | Enterprise store | Redux Toolkit | Middleware, devtools, large teams |
    | Server cache | TanStack Query | API data, pagination, mutations |
    | Memoization | useMemo / useCallback | Expensive calc or stable ref |
    | Compound comps | Context + cloneElement | API |
    | Error handling | ErrorBoundary | Catch render crashes |
    | Security | DOMPurify, CSRF tokens | User HTML, state-changing requests |
    | Perf | React.memo, lazy, Suspense | Prevent re-renders, reduce bundle |

    Anti-Patterns

  • Prop drilling >3 levels → use Context or state library
  • Huge useEffect deps → split into focused effects or useReducer
  • Stale closures in setInterval → use useRef for mutable latest value
  • Inline object/array props → causes re-renders; extract to useMemo
  • Index as key → use stable unique IDs
  • Uncaught promise rejections → always .catch() or error boundary
  • Direct DOM manipulation → let React own the DOM; use refs sparingly
  • dangerouslySetInnerHTML without sanitization → always use DOMPurify