Frontend Engineering

React & TypeScript Quiz

React & TypeScript Quiz — Study Guide

React & TypeScript: Comprehensive Study Guide

React powers millions of modern web apps, and TypeScript makes those apps safer and easier to maintain. Together, they form one of the most in-demand skill sets in frontend development. This guide covers everything from core hooks to performance optimization, accessibility, and Next.js server components — exactly what you need to ace the quiz.


Hooks: The Foundation of Modern React

Hooks let you use state and lifecycle features inside function components. Every hook starts with use.

useEffect

useEffect runs side effects (data fetching, subscriptions, DOM manipulation) after rendering.

useEffect(() => {
  console.log("Runs after every render");
});

useEffect(() => { console.log("Runs ONCE on mount — empty array = no dependencies"); }, []);

useEffect(() => { console.log("Runs when userId changes"); }, [userId]);

Key rule: An empty dependency array [] means the effect runs only once, after the initial render — like componentDidMount.

useRef

useRef gives you a mutable container that does not trigger re-renders when changed. Use it for:

  • Accessing DOM elements directly
  • Storing values that persist across renders without causing updates
  • const inputRef = useRef<HTMLInputElement>(null);

    const focusInput = () => { inputRef.current?.focus(); // Direct DOM access };

    useReducer

    An alternative to useState for complex state logic — similar to Redux but built-in.

    type Action = { type: "increment" } | { type: "decrement" };

    function reducer(state: number, action: Action): number { switch (action.type) { case "increment": return state + 1; case "decrement": return state - 1; } }

    const [count, dispatch] = useReducer(reducer, 0);


    Performance Optimization

    React.memo

    React.memo is a higher-order component that prevents re-rendering when props haven't changed (shallow comparison).

    const Button = React.memo(({ label }: { label: string }) => {
      return <button>{label}</button>;
    });

    ⚠️ React.memo does not help if you pass new object/function references every render. Combine with useCallback for functions.

    useMemo

    useMemo caches the result of a computation between renders.

    const sortedList = useMemo(() => {
      return [...items].sort((a, b) => a.name.localeCompare(b.name));
    }, [items]);

    When NOT to use useMemo:

  • For simple, fast calculations (the overhead isn't worth it)
  • When the dependency array changes every render anyway
  • Prematurely — always profile first

  • TypeScript in React

    type vs interface

    Featuretypeinterface
    ExtendingVia & intersectionVia extends
    Declaration merging❌ No✅ Yes
    Union types✅ Yes❌ No
    General recommendationPrimitives, unionsObject shapes, classes
    type Status = "loading" | "success" | "error"; // Union — use type

    interface User { id: number; name: string; }

    Both work for most component props, but interface is preferred for public APIs due to declaration merging.


    Forms and Inputs

    Controlled inputs bind value to state; uncontrolled inputs use useRef.

    const [email, setEmail] = useState("");

    <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />

    For TypeScript, type your event handlers: React.ChangeEvent.


    Data Fetching & Async

    Fetching Inside useEffect

    useEffect(() => {
      const controller = new AbortController();
      fetch("/api/data", { signal: controller.signal })
        .then(res => res.json())
        .then(setData);
      return () => controller.abort(); // Cleanup on unmount
    }, []);

    Suspense

    Suspense lets you declaratively handle loading states for async operations:

    <Suspense fallback={<Spinner />}>
      <UserProfile />
    </Suspense>


    React Internals: Virtual DOM & Reconciliation

    React keeps a Virtual DOM — a lightweight JavaScript copy of the real DOM. When state changes:

  • React creates a new Virtual DOM tree
  • Reconciliation diffs the old and new trees (using the "diffing algorithm")
  • Only the changed nodes are updated in the real DOM
  • Lists and Keys: Always provide stable key props in lists so React can efficiently reconcile:

    {users.map(user => (
      <UserCard key={user.id} user={user} /> // ✅ stable ID
    ))}

    Never use array index as a key if the list can reorder.


    Next.js & Server Components

    Next.js 13+ introduced React Server Components (RSC):

    Server ComponentsClient Components
    Runs onServer onlyBrowser (+ server for SSR)
    Can use hooks❌ No✅ Yes
    Can fetch data✅ DirectlyVia useEffect / SWR
    Bundle size impactNoneAdds to JS bundle
    Mark client components with "use client" at the top of the file.


    CSS in React

    Common approaches:

  • CSS Modules — scoped styles via styles.className
  • Tailwind CSS — utility classes
  • CSS-in-JS (styled-components, Emotion) — styles in JavaScript
  • Inline stylesstyle={{ color: "red" }} (camelCase properties)

  • Web Vitals & Accessibility

    Core Web Vitals

  • LCP (Largest Contentful Paint) — loading performance
  • FID/INP — interactivity responsiveness
  • CLS (Cumulative Layout Shift) — visual stability
  • Accessibility & ARIA

    Always prefer semantic HTML. Use ARIA only when native elements aren't enough:

    // ✅ Prefer semantic HTML
    <button onClick={handleClick}>Submit</button>

    // When you must use a div as interactive element <div role="button" tabIndex={0} aria-label="Close dialog" onClick={handleClose} onKeyDown={handleKeyDown} />

    Key ARIA attributes:

  • aria-label — names an element for screen readers
  • aria-hidden="true" — hides decorative elements
  • aria-live — announces dynamic content updates
  • role — defines the element's semantic role

  • Key Takeaways

  • useEffect with [] runs once after mount; always clean up subscriptions to avoid memory leaks.
  • React.memo + useMemo prevent unnecessary work — but only use them when you've identified a real performance problem, not preemptively.
  • TypeScript's type vs interface: use type for unions/primitives, interface for object shapes that may be extended.
  • Reconciliation relies on stable key props in lists — using array indexes as keys causes subtle bugs when lists reorder.
  • Accessibility isn't optional: use semantic HTML first, ARIA attributes second, and always ensure keyboard navigability.