React Fundamentals and Cheatsheet: Architecture, Patterns, and Comparison with Angular, Vue, Svelte
React is a JavaScript library for building user interfaces using a component model and a virtual DOM. This article covers React fundamentals, component architecture, common patterns, state management, routing, data fetching, tooling, and a practical cheatsheet, followed by a comparison with Angular, Vue, and Svelte so architects and developers can choose the right tool for the job.
For a hands-on reference, also see the DevOps Ready Reckoner for the surrounding build and deployment toolchain that packages a React app for production.
What is React?
React is a declarative JavaScript library for building component-based user interfaces. It was created by Meta and is maintained by Meta and the open-source community.
Important distinctions:
- React is a library, not a full framework.
- It concerns itself primarily with the view layer.
- Routing, state management, and HTTP are handled by separate libraries.
- React renders to a virtual DOM and updates the real DOM efficiently.
React can render to the web (react-dom), to native mobile via React Native, and to servers via server components and Node.js rendering.
Architecture and the Virtual DOM
The central concept in React is a component tree. When state changes, React:
1. Builds a new virtual DOM tree
2. Diffs (reconciles) it against the previous tree
3. Computes a minimal set of DOM mutations
4. Applies those mutations in a single batch
This "render, diff, commit" loop is what makes React predictable and efficient:
React Component Tree
|
+-----------------------+
| Virtual DOM (current) |
+-----------------------+
|
Reconciliation / diffing
|
+-----------------------+
| Virtual DOM (new) |
+-----------------------+
|
Commit phase
|
Real DOM (updated)
Components, Props, and JSX
A component is a reusable, isolated piece of UI. Define one with a function:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// or arrow form
const Welcome = (props) => (
<h1>Hello, {props.name}</h1>
);
JSX lets you write HTML-like syntax in JavaScript:
<div className="card">
<h2>{title}</h2>
<p>{description}</p>
</div>
React.createElement, which returns React elements — plain objects. At runtime React reads those objects and produces DOM nodes. This is why JSX is optional but strongly recommended for readability.Composition with children
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
{children}
</div>
);
}
// Usage: props.children is injected between the tags
<Card title="Welcome">
<p>Body content</p>
</Card>
State, Hooks, and Lifecycle
Components manage their own state with the useState Hook:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
Side effects and lifecycle are handled by useEffect:"
import { useState, useEffect } from 'react';
function Timer() {
const [now, setNow] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => setNow(new Date()), 1000);
return () => clearInterval(id); // cleanup on unmount
}, []);
return <time>{now.toLocaleTimeString()}</time>;
}
Common Component Patterns
Container / Presentational split
Separate logic from markup. The container owns state and behavior; the presentational component receives data via props.
// Presentational
function UserList({ users, onEdit }) {
return (
<ul>
{users.map(u => (
<li key={u.id}>
{u.name} <button onClick={() => onEdit(u)}>Edit</button>
</li>
))}
</ul>
);
}
// Container
function UserContainer() {
const [users, setUsers] = useState([]);
const onEdit = (u) => navigate(`/users/${u.id}`);
useEffect(() => { fetchUsers().then(setUsers); }, []);
return <UserList users={users} onEdit={onEdit} />;
}
Custom Hooks
Reuse stateful logic across components:
function useForm(initial) {
const [values, setValues] = useState(initial);
const handleChange = (e) =>
setValues({ ...values, [e.target.name]: e.target.value });
const reset = () => setValues(initial);
return { values, handleChange, reset };
}
HOC and render props
Still useful for cross-cutting concerns such as authentication gating:
function withAuth(Component) {
return function Authenticated(props) {
const { user } = useContext(AuthContext);
return user ? <Component {...props} /> : <Login />;
};
}
The React Ecosystem
| Concern | Library / Tool |
|---|---|
| Language | JavaScript, TypeScript |
| Build | Vite, webpack, esbuild, Turbopack |
| Routing | react-router, Next.js, Remix |
| State (local) | useState, useReducer, useContext |
| State (global) | Zustand, Jotai, Redux, React Query |
| Data fetching | React Query, SWR, fetch, axios |
| Styling | CSS Modules, Tailwind, styled-components, Emotion |
| Component libraries | MUI, Ant Design, Radix, Chakra UI |
| Forms | React Hook Form, Formik, Zod |
| Testing | Vitest, Jest, React Testing Library |
| Deployment | Next.js, Vite build, Docker |
| Type checking | TypeScript |
Routing: React Router
import { BrowserRouter, Routes, Route, Link, useParams, useNavigate } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/users/1">User 1</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="users/:id" element={<User />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
function User() {
const { id } = useParams();
// fetch user by id...
return <h1>User {id}</h1>;
}
Data Fetching
Fetch inside useEffect, or in React 18+ with Suspense boundaries:
import { useState, useEffect } from 'react';
function Todos() {
const [todos, setTodos] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('/api/todos')
.then((res) => res.json())
.then((data) => setTodos(data))
.catch((err) => setError(err))
.finally(() => setLoading(false));
}, []);
if (loading) return <p>Loadingβ¦</p>;
if (error) return <p>Error: {error.message}</p>;
return <ul>{todos.map(t => <li key={t.id}>{t.title}</li>)}</ul>;
}
Tooling: Vite, TypeScript, Testing
# Scaffold a typed React project
npm create vite@latest myapp -- --template react-ts
cd myapp
npm install
npm run dev
createRoot, startTransition, useId, useSyncExternalStore. Migrate away from the legacy ReactDOM.render API.React Cheatsheet
Core API
| API | Purpose |
|---|---|
useState(initial) | Local state: [value, setValue] |
useEffect(fn, deps) | Side effects and re-run on dep change |
useContext(ctx) | Read context value |
useReducer(reducer, init) | State logic via reducer |
useMemo(fn, deps) | Memoise expensive value |
useCallback(fn, deps) | Memoise function reference |
useRef(initial) | Mutable ref: .current |
useId() | Unique ID (React 18+) |
startTransition(fn) | Non-urgent update (React 18+) |
Component definition
| Form | Syntax |
|---|---|
| Function component | function App() { return <div />; } |
| Props destructuring | function App({ name, count }) {} |
| Typed props (TS) | interface Props { name: string; count?: number; } |
| Children | function App({ children }) {} |
| Default export | export default App; |
JSX essentials
| Need | JSX |
|---|---|
| DOM element | <div></div> |
| Component | <App /> |
| Conditional | {cond ? <A /> : <B />} |
| List | {items.map(i => <li key={i.id}>)} |
| Event handler | onClick={handleClick} |
| Two-way input | <input value={v} onChange={e => setV(e.target.value)} /> |
| Key attribute | <li key>... |
React Router quick reference
| Element | Purpose |
|---|---|
<BrowserRouter> | Browser history router |
<Routes> / <Route> | Route matching tree |
useParams | Read URL path params |
useNavigate | Programmatic navigation |
<Link> / <NavLink> | Accessible anchor links |
Package.json essentials
"dependencies": {
"react": "^18.3.0",
"react-dom": "^18.3.0",
"react-router-dom": "^6.23.0"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint src --fix",
"test": "vitest"
}
key to list items, memoise callbacks with useCallback, avoid inline objects in props, lift expensive renders into useMemo, and split bundles with React.lazy + <Suspense>.React vs Angular vs Vue vs Svelte
| Dimension | React | Angular | Vue | Svelte |
|---|---|---|---|---|
| Category | Library | Full framework | Progressive framework | Compiler |
| Built by | Meta | Evan You + community | Rich Harris + community | |
| Language | JS/TS + JSX | TypeScript | TS + Templates | TS + Templates |
| Bundled size (min+gz, approx.) | ~43 KB (react-dom) | ~160 KB | ~33 KB | ~1-5 KB (runtime) |
| Learning curve | Moderate | High | Low-Moderate | Low-Moderate |
| Data binding | One-way | Two-way | Two-way | Reactive, compile-time |
| State management | Context/Zustand/Redux/RTK | Services + RxJS/NgRx | Pinia / Vuex | Stores / runes |
| Routing | react-router | @angular/router | vue-router | SvelteKit routing |
| Routing | SSR via Next.js | Angular Universal | Nuxt / SSR | SvelteKit (SSR) |
| Removal of runtime | No | No | Yes | |
| HMR / DX | Excellent (Vite) | Good (CLI) | Excellent | Excellent |
| Community / jobs | Largest | Large | Growing | Smaller but growing |
| IEC use cases | Web apps, SSR, native | Enterprise apps | Progressive web apps | Small to large web apps |
Comparison: data flow
# React (one-way props)
function Child({ value, onChange }) {
return <button onClick={() => onChange(value + 1)}>{value}</button>;
}
# Vue (v-model two-way sugar)
<button @click="value++">{{ value }}</button>
When to Choose React
| Scenario | React fit |
|---|---|
| Large team / hiring market | β Strong job market |
| React Native for mobile | β Shared component model |
| SSR + SEO | β Next.js built-in |
| Incremental adoption | β Mount into a single DOM node |
| Enterprise framework preference | β Angular may fit better |
| Minimal bundle / no runtime | β Svelte is smaller |
| Rapid prototype | β Vite + React |
Conclusion
React is a small, fast, component-based view library. Its power comes from a vast ecosystem and the discipline of hooks and unidirectional data flow. Master the fundamentals — components, props, state, effects, and composition — and the rest of the ecosystem becomes straightforward to adopt.
Do not chase every library. Pick one routing solution (React Router or Next.js), one state story (useState/context locally, one global store for cross-component state), one styling approach (CSS Modules or Tailwind), and one testing strategy (React Testing Library). Consistency is what turns tooling into reliability in production.