React basics for 2026.
Function component
function Hello({ name }: { name: string }) {
return <h1>Hello {name}</h1>;
}
<Hello name="World" />
Always start with function components. Class components are legacy.
JSX
const el = <div className="box" onClick={handle}>{text}</div>;
// Attributes use camelCase: className, htmlFor, tabIndex
// Curly braces for expressions
// Self-close: <img /> not <img>
Fragments
return (
<>
<h1>title</h1>
<p>body</p>
</>
);
// Or React.Fragment for keys
return items.map((it) => (
<React.Fragment key={it.id}>
<dt>{it.term}</dt>
<dd>{it.def}</dd>
</React.Fragment>
));
Conditional rendering
{cond && <Thing />}
{cond ? <A /> : <B />}
{cond && cond2 && <Thing />}
// Avoid number 0 leak:
{count > 0 && <Badge n={count} />} // safe
{count && <Badge n={count} />} // BAD: 0 renders "0"
Lists
{items.map((it) => (
<li key={it.id}>{it.name}</li>
))}
Always provide key. Use stable IDs, not index (unless list is truly static).
Event handlers
<button onClick={() => doIt(id)}>click</button>
<form onSubmit={(e) => { e.preventDefault(); ... }}>
<input onChange={(e) => setVal(e.target.value)} />
useState
const [count, setCount] = useState(0);
setCount(count + 1); // direct
setCount((c) => c + 1); // updater form (preferred when based on prev)
Lazy initial state:
const [data, setData] = useState(() => expensiveInit());
useEffect
useEffect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, [tick]);
Three forms:
[]— once after mount[a, b]— when a or b changes- (no array) — after every render
Cleanup runs before next effect AND on unmount.
Controlled inputs
const [name, setName] = useState("");
<input value={name} onChange={(e) => setName(e.target.value)} />
Props
function Card({ title, children }: { title: string; children: React.ReactNode }) {
return <div><h2>{title}</h2>{children}</div>;
}
<Card title="Hello">
<p>Body</p>
</Card>
Spread props
<Button {...rest} />
// Pass through HTML attrs:
function Btn({ children, ...rest }: React.ComponentProps<"button">) {
return <button {...rest}>{children}</button>;
}
Refs (uncontrolled)
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => { inputRef.current?.focus(); }, []);
<input ref={inputRef} />
Context
const ThemeContext = createContext<"light" | "dark">("light");
function App() {
return (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
);
}
function Child() {
const theme = useContext(ThemeContext);
return <div>{theme}</div>;
}
Lifting state
When two siblings need shared state, move it to their parent and pass via props.
Composition over inheritance
function Modal({ children, onClose }: { children: React.ReactNode; onClose: () => void }) {
return <div className="modal" onClick={onClose}>{children}</div>;
}
<Modal onClose={close}>
<UserForm />
</Modal>
Strict Mode
<React.StrictMode>
<App />
</React.StrictMode>
Double-invokes components in dev to surface impure renders. Keep enabled.
Keys gotcha
{items.map((it, i) => <li key={i}>{it}</li>)} // BAD if list reorders
{items.map((it) => <li key={it.id}>{it.name}</li>)} // GOOD
Reordering with index keys causes subtle state-bleed bugs.
Pure rendering
Components must be pure: same props → same output, no side effects during render.
// BAD
function Bad() {
fetch("/x"); // side effect during render
return <div />;
}
// GOOD
function Good() {
useEffect(() => { fetch("/x"); }, []);
return <div />;
}
Common mistakes
- Mutating state directly —
arr.push(x); setArr(arr)doesn’t re-render. UsesetArr([...arr, x]). - Using index as key in reorderable list.
- Setting state in render — infinite loop.
- Forgetting deps array — runs every render.
useStatecallback —setCount(count + 1)twice in same tick only increments once.
Read this next
If you want my React + TS starter, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .