Skip to content

React cheatsheet from first components and hooks to reusable architecture, performance, testing, and advanced patterns.

React is a JavaScript library for building user interfaces from reusable components. You describe what the UI should look like for the current data, and React updates the browser when that data changes.

ConceptPurpose
ComponentReusable UI function that returns JSX
PropsInputs passed from a parent component
StateData a component remembers between renders
Event handlerCode triggered by a user action
HookFunction such as useState or useEffect that adds React behavior
ContextShared value available to nested components
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
export default function App() {
return <Greeting name="React" />;
}

React component names begin with an uppercase letter. HTML tags stay lowercase, such as <button> and <section>.


JSX lets JavaScript expressions appear inside markup using {}.

const user = {
name: "Asha",
avatarUrl: "/images/asha.png",
};
export default function Profile() {
return (
<section className="profile">
<img src={user.avatarUrl} alt={`${user.name}'s avatar`} />
<h2>{user.name}</h2>
<p>{user.name} is learning React.</p>
</section>
);
}
JSX RuleExample
Return one parent elementWrap siblings in <div> or <>...</>
Use className for CSS classes<div className="card" />
Close every tag<img src={url} alt="" />
Put JavaScript expressions in braces<p>{price * quantity}</p>
Use camelCase DOM properties<button onClick={save}>Save</button>
Write inline styles as objects<p style={{ color: "tomato" }}>Alert</p>
function Avatar({ name, imageUrl }) {
return <img className="avatar" src={imageUrl} alt={name} />;
}
function UserCard({ user }) {
return (
<article className="card">
<Avatar name={user.name} imageUrl={user.imageUrl} />
<h3>{user.name}</h3>
<p>{user.role}</p>
</article>
);
}
export default function App() {
const user = {
name: "Maya",
role: "Frontend Developer",
imageUrl: "/images/maya.png",
};
return <UserCard user={user} />;
}

Use a fragment when elements need grouping without adding an extra DOM node:

function Name() {
return (
<>
<dt>React</dt>
<dd>UI library</dd>
</>
);
}

Props are read-only inputs. A child component must not modify the props it receives.

function Badge({ label, variant = "default" }) {
return <span className={`badge badge-${variant}`}>{label}</span>;
}
export default function App() {
return (
<div>
<Badge label="New" variant="success" />
<Badge label="Draft" />
</div>
);
}
function Button({ children, onClick, disabled = false }) {
return (
<button type="button" onClick={onClick} disabled={disabled}>
{children}
</button>
);
}
function Alert({ title, message }) {
return (
<div role="alert">
<strong>{title}</strong>
<p>{message}</p>
</div>
);
}
PatternMeaning
childrenContent nested inside a component
onSomethingCallback prop supplied by a parent
Default valueFallback when a prop is omitted
Object propRelated data passed together, such as user

React uses ordinary JavaScript conditions.

function LoginStatus({ user }) {
if (!user) {
return <button>Log in</button>;
}
return <p>Welcome back, {user.name}.</p>;
}
function Inbox({ unreadCount, messages }) {
return (
<section>
{unreadCount > 0 && <p>You have {unreadCount} unread messages.</p>}
{messages.length === 0 ? (
<p>No messages yet.</p>
) : (
<p>Choose a message to read.</p>
)}
</section>
);
}
NeedUse
Exit early for a whole viewif (...) return ...
Show one of two elementscondition ? a : b
Show an element only when truecondition && element

Be careful with count && <p>...</p> when count may be 0; React can render the 0. Prefer count > 0 && ....


Use map() to turn data into components. Every item created in a list needs a stable key.

function TodoList({ todos }) {
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>
{todo.completed ? "Done: " : "Todo: "}
{todo.title}
</li>
))}
</ul>
);
}
Good KeyRisky Key
Database ID: todo.idArray index when items can move
Stable slug: post.slugMath.random()
Persistent generated IDValues that change every render

Keys tell React which item is which across insertions, removals, and reordering. Keys are not passed into the child as a normal prop:

<TodoItem key={todo.id} id={todo.id} todo={todo} />

State stores information that affects rendering. Updating state asks React to render again.

import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
}
return (
<div>
<p>Count: {count}</p>
<button type="button" onClick={increment}>
Add one
</button>
</div>
);
}
EventExample
Click<button onClick={handleClick}>Save</button>
Input change<input onChange={handleChange} />
Form submission<form onSubmit={handleSubmit}>
Keyboard input<input onKeyDown={handleKeyDown} />
Focus / blur<input onFocus={open} onBlur={close} />

Pass a function, not the result of calling one:

<button onClick={handleSave}>Save</button> // Correct
<button onClick={() => deleteItem(item.id)}>Delete</button>

When the next value depends on the previous one, use the updater form:

function incrementThreeTimes() {
setCount((current) => current + 1);
setCount((current) => current + 1);
setCount((current) => current + 1);
}
const [user, setUser] = useState({ name: "Sam", role: "Student" });
function promoteUser() {
setUser((current) => ({
...current,
role: "Mentor",
}));
}
const [todos, setTodos] = useState([]);
function addTodo(todo) {
setTodos((current) => [...current, todo]);
}
function removeTodo(id) {
setTodos((current) => current.filter((todo) => todo.id !== id));
}
Avoid MutationUse Instead
array.push(item)[...array, item]
array.splice(index, 1)array.filter(...)
object.name = "New"{ ...object, name: "New" }
array.sort() on state[...array].sort()

A controlled input gets its value from state and reports edits through an event handler.

import { useState } from "react";
export default function SignupForm() {
const [form, setForm] = useState({
name: "",
email: "",
});
function handleChange(event) {
const { name, value } = event.target;
setForm((current) => ({
...current,
[name]: value,
}));
}
function handleSubmit(event) {
event.preventDefault();
console.log("Submitted:", form);
}
return (
<form onSubmit={handleSubmit}>
<label>
Name
<input name="name" value={form.name} onChange={handleChange} />
</label>
<label>
Email
<input
type="email"
name="email"
value={form.email}
onChange={handleChange}
/>
</label>
<button type="submit">Create account</button>
</form>
);
}
// Checkbox
<input
type="checkbox"
checked={accepted}
onChange={(event) => setAccepted(event.target.checked)}
/>
// Select
<select value={country} onChange={(event) => setCountry(event.target.value)}>
<option value="in">India</option>
<option value="us">United States</option>
</select>
// Textarea
<textarea value={notes} onChange={(event) => setNotes(event.target.value)} />

Use semantic labels and native validation attributes such as required, type="email", and minLength before building complex custom validation.


When two components must stay in sync, move their state to the nearest common parent and pass values and callbacks as props.

import { useState } from "react";
function SearchBox({ query, onQueryChange }) {
return (
<input
value={query}
onChange={(event) => onQueryChange(event.target.value)}
placeholder="Search products"
/>
);
}
function ProductList({ products, query }) {
const filtered = products.filter((product) =>
product.name.toLowerCase().includes(query.toLowerCase()),
);
return (
<ul>
{filtered.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
export default function ProductsPage({ products }) {
const [query, setQuery] = useState("");
return (
<>
<SearchBox query={query} onQueryChange={setQuery} />
<ProductList products={products} query={query} />
</>
);
}

Do not put derived values such as filtered into state unless there is a specific reason. Derive them from current props and state during rendering.


Effects synchronize a component with something outside React: network requests, browser APIs, subscriptions, timers, or third-party widgets.

import { useEffect, useState } from "react";
export default function Clock() {
const [time, setTime] = useState(() => new Date());
useEffect(() => {
const intervalId = window.setInterval(() => {
setTime(new Date());
}, 1000);
return () => {
window.clearInterval(intervalId);
};
}, []);
return <time>{time.toLocaleTimeString()}</time>;
}
CodeWhen the Effect Runs
useEffect(fn)After every render
useEffect(fn, [])After mounting; cleanup on unmount
useEffect(fn, [roomId])After mounting and when roomId changes
import { useEffect, useState } from "react";
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
async function loadUser() {
setLoading(true);
setError(null);
try {
const response = await fetch(`/api/users/${userId}`, {
signal: controller.signal,
});
if (!response.ok) {
throw new Error("Unable to load user");
}
setUser(await response.json());
} catch (caughtError) {
if (caughtError.name !== "AbortError") {
setError(caughtError.message);
}
} finally {
if (!controller.signal.aborted) {
setLoading(false);
}
}
}
loadUser();
return () => controller.abort();
}, [userId]);
if (loading) return <p>Loading...</p>;
if (error) return <p role="alert">{error}</p>;
return <h2>{user.name}</h2>;
}
TaskBetter Approach
Calculate a value from props/stateCalculate during render
Respond to a button clickPut logic in the event handler
Reset an entire subtree for a new itemGive it a different key
Expensive derived calculationUse useMemo only after measuring need

A ref stores a mutable value without causing a render. It can also point at a DOM element.

import { useRef } from "react";
export default function SearchForm() {
const inputRef = useRef(null);
function focusSearch() {
inputRef.current.focus();
}
return (
<>
<input ref={inputRef} type="search" />
<button type="button" onClick={focusSearch}>
Focus search
</button>
</>
);
}
const timeoutRef = useRef(null);
function showMessage() {
window.clearTimeout(timeoutRef.current);
timeoutRef.current = window.setTimeout(() => {
setVisible(false);
}, 2000);
}
Use State WhenUse Ref When
The screen must update after a value changesThe value should persist without rendering
Value is rendered in JSXYou need a DOM node, timer ID, or previous value

Do not read or write ref.current during rendering to drive visible UI; use state for that.


Context supplies a value to deeply nested components without passing the same prop through every layer. Good candidates include theme, authenticated user, locale, or small app-wide configuration.

import { createContext, useContext, useState } from "react";
const ThemeContext = createContext(null);
function ThemeButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button type="button" onClick={toggleTheme}>
Current theme: {theme}
</button>
);
}
export default function App() {
const [theme, setTheme] = useState("light");
function toggleTheme() {
setTheme((current) => (current === "light" ? "dark" : "light"));
}
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<main className={theme}>
<ThemeButton />
</main>
</ThemeContext.Provider>
);
}

Context does not automatically replace all state management. Frequently changing large values can cause many context consumers to render; split contexts or choose a dedicated state tool when the application needs it.


useReducer is useful when several state changes belong to one workflow or when updates are easier to express as actions.

import { useReducer } from "react";
function todoReducer(state, action) {
switch (action.type) {
case "added":
return [
...state,
{ id: action.id, title: action.title, completed: false },
];
case "toggled":
return state.map((todo) =>
todo.id === action.id
? { ...todo, completed: !todo.completed }
: todo,
);
case "deleted":
return state.filter((todo) => todo.id !== action.id);
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
export default function TodoApp() {
const [todos, dispatch] = useReducer(todoReducer, []);
function addExample() {
dispatch({ type: "added", id: crypto.randomUUID(), title: "Learn hooks" });
}
return (
<>
<button type="button" onClick={addExample}>
Add todo
</button>
{todos.map((todo) => (
<label key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => dispatch({ type: "toggled", id: todo.id })}
/>
{todo.title}
</label>
))}
</>
);
}
Prefer useStatePrefer useReducer
A few independent valuesRelated transitions across a complex object
Straightforward settersActions help describe user intent
Simple form controlsState logic benefits from isolated tests

Custom hooks share stateful behavior, not rendered markup. Their names begin with use.

import { useEffect, useState } from "react";
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
function updateOnlineStatus() {
setIsOnline(navigator.onLine);
}
window.addEventListener("online", updateOnlineStatus);
window.addEventListener("offline", updateOnlineStatus);
return () => {
window.removeEventListener("online", updateOnlineStatus);
window.removeEventListener("offline", updateOnlineStatus);
};
}, []);
return isOnline;
}
function SaveButton() {
const isOnline = useOnlineStatus();
return (
<button type="button" disabled={!isOnline}>
{isOnline ? "Save" : "Offline"}
</button>
);
}
RuleReason
Call hooks at the top levelReact relies on a consistent call order
Call hooks only from React components or custom hooksHook state belongs to React rendering
Include reactive values used by effects in dependenciesAvoid stale values and synchronization bugs

Do not call hooks inside conditions, loops, nested functions, or ordinary utility functions.


function Card({ title, children }) {
return (
<section className="card">
<h2>{title}</h2>
<div>{children}</div>
</section>
);
}
function Dashboard() {
return (
<Card title="Revenue">
<p>$4,200 this month</p>
<button type="button">View report</button>
</Card>
);
}
function Dialog({ title, children, actions }) {
return (
<section role="dialog" aria-labelledby="dialog-title">
<h2 id="dialog-title">{title}</h2>
<div>{children}</div>
<footer>{actions}</footer>
</section>
);
}
function DeleteDialog() {
return (
<Dialog
title="Delete file?"
actions={
<>
<button type="button">Cancel</button>
<button type="button">Delete</button>
</>
}
>
This action cannot be undone.
</Dialog>
);
}
SmellImprovement
One component loads, filters, edits, and displays everythingExtract meaningful sections or hooks
Many boolean props that conflictUse clear variants or separate components
Child needs distant sibling dataLift shared state to their common parent
Copy-pasted stateful behaviorExtract a custom hook

Favor composition over building one enormous “do everything” component.


Small examples can fetch in an effect, but real applications often need caching, request deduplication, retries, background refreshing, routing integration, and server rendering.

RequirementTypical Direction
One client-only request in a small widgetuseEffect with loading/error/abort handling
Route-level dataUse the data-loading API of the selected framework/router
Cached shared API dataUse a server-state library selected by the project
Server-rendered applicationFetch through the framework’s server/data primitives

Always represent the main states:

function Results({ data, loading, error }) {
if (loading) return <p>Loading results...</p>;
if (error) return <p role="alert">Could not load results.</p>;
if (data.length === 0) return <p>No matching results.</p>;
return (
<ul>
{data.map((result) => (
<li key={result.id}>{result.name}</li>
))}
</ul>
);
}

Avoid storing the same server data in several unrelated components without a deliberate synchronization strategy.


React does not include a full browser router by itself. Applications typically use a framework or routing library.

Routing NeedConcept
Display UI for a URLRoute
Move without full page reloadLink/navigation component
Read /products/:idRoute parameter
Read ?query=reactSearch parameter
Protect user-only pagesAuthentication/authorization boundary
Share page layoutNested layout route

An application with pages might be organized as:

src/
components/
Button.jsx
ProductCard.jsx
pages/
HomePage.jsx
ProductPage.jsx
hooks/
useProducts.js
App.jsx

Follow the chosen framework or router for route definition, loading, error pages, and navigation rather than inventing URL state by hand.


React can work with ordinary CSS or a styling system chosen by the project.

import "./Button.css";
function Button({ variant = "primary", children }) {
return (
<button className={`button button-${variant}`} type="button">
{children}
</button>
);
}
.button {
border: 0;
border-radius: 0.5rem;
padding: 0.5rem 1rem;
}
.button-primary {
background: #2563eb;
color: white;
}
function Tab({ active, children }) {
const className = active ? "tab tab-active" : "tab";
return <button className={className}>{children}</button>;
}
ApproachUseful For
Plain CSSSimple sites and shared global design tokens
CSS ModulesLocally scoped component classes
Utility classesRapid composition with a configured design system
CSS-in-JSProjects already built around runtime or compiled styling

Choose one consistent styling approach for a project rather than mixing systems casually.


Accessible HTML usually starts with choosing the correct native element.

function AccessibleForm() {
const errorId = "email-error";
return (
<form>
<label htmlFor="email">Email address</label>
<input
id="email"
name="email"
type="email"
aria-describedby={errorId}
required
/>
<p id={errorId}>Enter a valid email address.</p>
<button type="submit">Subscribe</button>
</form>
);
}
DoAvoid
Use <button> for actionsClickable <div> elements
Associate labels with inputsPlaceholder-only form labels
Add meaningful image alt textRepeating decorative image details
Preserve keyboard focus visibilityRemoving outlines without replacement
Use aria-* only where neededReplacing native semantics with ARIA

For modal dialogs, menus, tabs, and comboboxes, use tested accessible component primitives or implement the full keyboard and focus behavior carefully.


Start with clear code and measure actual slow interactions before memoizing.

import { useMemo } from "react";
function ProductTable({ products, query }) {
const visibleProducts = useMemo(() => {
return products.filter((product) =>
product.name.toLowerCase().includes(query.toLowerCase()),
);
}, [products, query]);
return visibleProducts.map((product) => (
<p key={product.id}>{product.name}</p>
));
}
import { memo, useCallback, useState } from "react";
const SaveButton = memo(function SaveButton({ onSave }) {
return (
<button type="button" onClick={onSave}>
Save
</button>
);
});
function Editor() {
const [document, setDocument] = useState({ title: "" });
const handleSave = useCallback(() => {
console.log("Saving", document);
}, [document]);
return <SaveButton onSave={handleSave} />;
}
ToolUse It When
memo(Component)A component renders often with unchanged props and rendering is costly
useMemo(fn, deps)A measured expensive calculation repeats unnecessarily
useCallback(fn, deps)Stable function identity matters for a memoized child or dependency
Lazy loadingA route or heavy component need not ship immediately

Memoization adds complexity and can be useless when props change each render. Profile first.

import { lazy, Suspense } from "react";
const ReportsPage = lazy(() => import("./ReportsPage.jsx"));
function App() {
return (
<Suspense fallback={<p>Loading report tools...</p>}>
<ReportsPage />
</Suspense>
);
}

Type props, event handlers, and reusable children explicitly where it improves clarity.

type Product = {
id: string;
name: string;
price: number;
};
type ProductCardProps = {
product: Product;
onAddToCart: (productId: string) => void;
};
export function ProductCard({ product, onAddToCart }: ProductCardProps) {
return (
<article>
<h2>{product.name}</h2>
<p>${product.price.toFixed(2)}</p>
<button type="button" onClick={() => onAddToCart(product.id)}>
Add to cart
</button>
</article>
);
}
import type { ChangeEvent, FormEvent, ReactNode } from "react";
type PanelProps = {
heading: string;
children: ReactNode;
};
function Panel({ heading, children }: PanelProps) {
return (
<section>
<h2>{heading}</h2>
{children}
</section>
);
}
function Search() {
function handleChange(event: ChangeEvent<HTMLInputElement>) {
console.log(event.target.value);
}
function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
</form>
);
}
NeedType
Renderable nested contentReactNode
Input change eventChangeEvent<HTMLInputElement>
Form submit eventFormEvent<HTMLFormElement>
Button click eventMouseEvent<HTMLButtonElement>
DOM input refuseRef<HTMLInputElement>(null)

Test what users observe and do: rendered text, labels, keyboard/click behavior, loading/error states, and outcomes of interactions.

Counter.jsx
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return (
<>
<p>Count: {count}</p>
<button type="button" onClick={() => setCount((value) => value + 1)}>
Increment
</button>
</>
);
}
// Counter.test.jsx - example using Testing Library style APIs
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Counter } from "./Counter";
test("increments the displayed count", async () => {
const user = userEvent.setup();
render(<Counter />);
await user.click(screen.getByRole("button", { name: "Increment" }));
expect(screen.getByText("Count: 1")).toBeInTheDocument();
});
TestVerify
RenderingMeaningful content and accessible roles appear
InteractionClicking, typing, or submitting changes visible behavior
Async stateLoading, success, empty, and error views work
Custom hook/reducerImportant state transitions are correct
Page flowCritical user paths work across components

Avoid asserting internal state variables or implementation details when a user-visible outcome expresses the requirement.


An error boundary prevents a rendering error in part of the component tree from blanking the entire interface. Error boundaries are commonly supplied by a framework/router or implemented as a class component.

import { Component } from "react";
class ErrorBoundary extends Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
console.error("UI rendering failed", error, info);
}
render() {
if (this.state.hasError) {
return <p role="alert">Something went wrong. Please try again.</p>;
}
return this.props.children;
}
}
function App() {
return (
<ErrorBoundary>
<Dashboard />
</ErrorBoundary>
);
}

Error boundaries handle errors while rendering descendants, but they do not replace handling failed requests or errors inside event handlers.


QuestionGuidance
Can it be calculated from existing state/props?Do not store it separately
Do several children edit it?Lift it to their common parent
Is it shared deeply but changes infrequently?Consider context
Does it have many named transitions?Consider a reducer
Is it remote cached data?Treat it as server state
Must it survive navigation or reload?Store it in URL or persistence layer as appropriate

React preserves state while the same component remains in the same position. Use a key to intentionally reset a form when its identity changes:

function MessageEditor({ selectedContact }) {
return <ChatForm key={selectedContact.id} contact={selectedContact} />;
}

A portal renders UI into a different DOM container, useful for modal overlays and tooltips:

import { createPortal } from "react-dom";
function Modal({ children }) {
return createPortal(
<div className="modal-backdrop">
<div role="dialog" aria-modal="true">
{children}
</div>
</div>,
document.body,
);
}

Portals change DOM placement, not React ownership: context and React event handling still follow the React tree.


Mistake or SymptomCauseFix
Component does not renderName begins lowercaseUse function Profile() and <Profile />
List input values move to wrong rowUnstable or index keysUse persistent unique item IDs
UI does not update after array changeState was mutatedReturn a new array/object
Infinite effect loopEffect updates a changing dependency each runRethink synchronization and dependencies
Handler runs during renderingCalled it in JSXPass onClick={handleClick} or a callback
Input switches between controlled/uncontrolledvalue starts undefinedInitialize to "" or consistently use uncontrolled input
Duplicate/out-of-date stateDerived data stored separatelyCalculate from source state/props
Memoization everywhere but no speedupOptimization added without measuringProfile, then optimize the slow path
Clicking a <div> is not keyboard accessibleNon-semantic interactive elementUse <button> or proper native element
// Wrong: mutating state
todos.push(newTodo);
setTodos(todos);
// Correct: new array
setTodos((current) => [...current, newTodo]);
// Wrong: invokes immediately while rendering
<button onClick={removeItem(id)}>Remove</button>
// Correct: invokes after click
<button onClick={() => removeItem(id)}>Remove</button>

Start simple and group code by feature as an application grows.

src/
app/
App.jsx
components/
Button.jsx
Modal.jsx
features/
cart/
CartButton.jsx
CartPage.jsx
useCart.js
products/
ProductCard.jsx
ProductsPage.jsx
lib/
api.js
styles/
globals.css
GuidelineWhy
Keep files close to the feature using themChanges are easier to understand
Promote truly shared UI into components/Avoid premature global abstractions
Put request clients and generic helpers in lib/Keep components focused on UI
Keep tests near feature code or in a consistent test treeMake coverage discoverable

Folder names matter less than predictable ownership and small, focused components.


export function Welcome({ name }) {
return <h1>Welcome, {name}!</h1>;
}
import { useState } from "react";
export function Toggle() {
const [open, setOpen] = useState(false);
return (
<>
<button type="button" onClick={() => setOpen((value) => !value)}>
{open ? "Hide" : "Show"} details
</button>
{open && <p>More information here.</p>}
</>
);
}
const [name, setName] = useState("");
<input value={name} onChange={(event) => setName(event.target.value)} />;
setItems((current) => [
...current,
{ id: crypto.randomUUID(), title: newTitle },
]);
setItems((current) =>
current.map((item) =>
item.id === editedItem.id ? { ...item, title: editedItem.title } : item,
),
);
setItems((current) => current.filter((item) => item.id !== id));
useEffect(() => {
const controller = new AbortController();
loadData({ signal: controller.signal });
return () => controller.abort();
}, []);

  1. Write components that return JSX.
  2. Pass data with props and render lists with stable keys.
  3. Handle clicks and input changes with useState.
  4. Build a controlled form and lift state between sibling components.
  1. Synchronize with external systems using useEffect and cleanup.
  2. Use refs for DOM actions and context for carefully selected shared values.
  3. Move complicated transitions into useReducer.
  4. Extract reusable stateful logic into custom hooks.
  5. Test interactions and loading/error states from the user’s perspective.
  1. Design state ownership deliberately: local, URL, context, reducer, or server state.
  2. Build reusable component APIs with composition and accessible behavior.
  3. Profile before using memoization or code splitting.
  4. Add error boundaries and resilient data/loading patterns.
  5. Integrate React with a routing and rendering architecture suited to the application.
Component: function Card({ title }) { return <h2>{title}</h2>; }
State: const [value, setValue] = useState(initialValue);
Update prior: setCount((count) => count + 1);
Event: <button onClick={handleClick}>Save</button>
Input: <input value={name} onChange={(e) => setName(e.target.value)} />
List: items.map((item) => <Row key={item.id} item={item} />)
Conditional: ready ? <Result /> : <Loading />
Effect: useEffect(() => { connect(); return disconnect; }, [roomId]);
Ref: const inputRef = useRef(null);
Context: const value = useContext(ThemeContext);
Reducer: const [state, dispatch] = useReducer(reducer, initialState);
Memoize: useMemo(() => expensiveWork(data), [data]);
Lazy load: const Page = lazy(() => import("./Page.jsx"));