A comprehensive developer's playbook for React. Exploring state machines, hooks, Context APIs, rendering optimizations, memoization, and React Server Components (RSC).
React uses a declarative approach, meaning you describe what the UI should look like for a given state, rather than manually updating DOM elements.
Hooks enable functional components to manage local state, context, references, and lifecycles:
useState: Manages local component state.useEffect: Runs side-effects (fetching data, setting up subscriptions) after rendering.useContext: Subscribes to context providers without drilling props down multiple levels.useRef: Retains persistent values that do not trigger re-renders, or accesses actual DOM elements.import React, { useState, useEffect } from "react";
export default function UserProfiler({ userId }) {
const [profile, setProfile] = useState(null);
useEffect(() => {
let active = true;
fetch(`/api/users/${userId}`)
.then((res) => res.json())
.then((data) => {
if (active) setProfile(data);
});
return () => {
active = false; // Cleanup to prevent state updates on unmounted component
};
}, [userId]);
if (!profile) return <div>Loading...</div>;
return <h1>Profile: {profile.name}</h1>;
}
The Context API provides a way to share values between components without explicitly passing props down through every level of the tree.
import React, { createContext, useContext, useState } from "react";
const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState("dark");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function ThemeButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
Current Mode: {theme}
</button>
);
}
Re-rendering components unnecessarily causes latency. React provides three tools to optimize this:
React.memo: A higher-order component that skips rendering a component if its props haven't changed.useMemo: Cache the result of expensive calculations.useCallback: Cache a function definition to prevent child components from re-rendering when the parent state changes.import React, { useState, useMemo, useCallback } from "react";
export function Calculator() {
const [num, setNum] = useState(0);
const [theme, setTheme] = useState("light");
// useMemo prevents recalculating on theme changes
const computedValue = useMemo(() => {
return expensiveCalculation(num);
}, [num]);
// useCallback prevents re-creating the function on render
const handleNumChange = useCallback((e) => {
setNum(Number(e.target.value));
}, []);
return (
<div>
<input type="number" value={num} onChange={handleNumChange} />
<p>Result: {computedValue}</p>
</div>
);
}
function expensiveCalculation(n) {
let count = 0;
for (let i = 0; i < 1000000000; i++) { count += n; }
return count;
}
Next.js App Router utilizes React Server Components:
"use client". They are sent to the client to run interactive features (using hooks like useState, useEffect).useEffect and useLayoutEffect?
useEffect runs asynchronously after the browser paints the screen, while useLayoutEffect runs synchronously before the browser paints, which is useful for measuring layout.