This document is a full, practical, and example-rich guide to React — intentionally structured like official documentation but focused on clarity, real-world usage, and modern best practices.
React is a JavaScript library for building user interfaces by composing small, reusable pieces called components. React focuses on the View layer and encourages declarative UI.
Key concepts:
- Components (functional & class)
- JSX
- One-way data flow
- Hooks
- Virtual DOM & efficient reconciliation
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run devimport React from 'react';
import ReactDOM from 'react-dom/client';
function App() {
return <h1>Hello React</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);JSX looks like HTML but runs in JavaScript. It compiles to React.createElement.
Rules:
- Must return a single parent element.
- Use
classNameinstead ofclass. - Use
htmlForinstead offor.
Example:
const name = 'Amina';
const el = <div className="card">Hello, {name.toUpperCase()}!</div>;function Greeting({ name }) {
return <h1>Hello, {name}</h1>;
}class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}Functional components with hooks are recommended for modern apps.
- Props: read-only data passed from parent.
- State: mutable data inside a component.
Example:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}Hooks allow functional components to manage state and lifecycle.
- Call hooks only at top level.
- Only call them inside React functions.
useStateuseEffectuseRefuseMemouseCallbackuseReduceruseContext
useEffect(() => {
const id = setInterval(() => setTime(t => t + 1), 1000);
return () => clearInterval(id);
}, []);useLayoutEffectuseIduseTransitionuseDeferredValue
React components (especially class components) go through a lifecycle of mounting, updating, and unmounting. Hooks provide equivalents for function components.
Occurs when the component is first rendered.
| Step | Class Method | Function Component Equivalent |
|---|---|---|
| Initialize state & props | constructor() |
First render + useState initialization |
| After component is added to the DOM | componentDidMount() |
useEffect(() => {...}, []) |
Example:
useEffect(() => {
console.log('Mounted');
}, []);Typical use cases:
- Fetch API data
- Subscribe to events
- Initialize timers
Happens every time the component re-renders due to changes.
| Step | Class Method | Function Equivalent |
|---|---|---|
| Component re-render | render() |
Function body executes again |
| After update | componentDidUpdate() |
useEffect(..., [deps]) |
Example:
useEffect(() => {
console.log('Counter changed:', counter);
}, [counter]);Use cases:
- Trigger actions when specific variable changes
- Sync UI with external system
| Step | Class Method | Function Equivalent |
|---|---|---|
| Component removed | componentWillUnmount() |
cleanup function in useEffect |
Example:
useEffect(() => {
const id = setInterval(() => console.log('Tick'), 1000);
return () => clearInterval(id); // cleanup on unmount
}, []);Use cases:
- Clearing timers
- Removing event listeners
- Closing connections
Used to control if component should re-render. Function alternative:
React.memo()useCallbackuseMemo
Rarely used. Lets state update when props change. Function alternative:
- manually update state in
useEffect
Called before DOM updates (e.g. capturing scroll position). Function alternative:
useLayoutEffect()
| Phase | Class | Function |
|---|---|---|
| Before initial render | constructor | function body + useState |
| After first render | componentDidMount | useEffect(..., []) |
| Before update | getDerivedStateFromProps | manual logic inside body |
| Capture DOM info | getSnapshotBeforeUpdate | useLayoutEffect |
| After update | componentDidUpdate | useEffect(..., [deps]) |
| Before destroy | componentWillUnmount | cleanup function |
Hooks unify lifecycle into simpler logic:
useEffect= mount + update + unmountuseLayoutEffect= runs synchronously before browser paint- Class lifecycle methods are still supported but not recommended for new projects.
Below is how React lifecycle flows for class components:
MOUNTING
-----------------------------------
constructor() → render() → componentDidMount()
↓
UPDATE
-----------------------------------
new props or setState()
↓
shouldComponentUpdate() → render() → componentDidUpdate()
↓
UNMOUNT
-----------------------------------
componentWillUnmount()
Functional Component Equivalent:
function render() { ... }
useEffect(() => {...}, []) → mount
useEffect(() => {...}, [deps]) → update
cleanup return → unmount
| Feature | Class Components | Function Components (Hooks) |
|---|---|---|
| State | this.state |
useState |
| Update logic | setState() |
State setter (e.g. setCount) |
| Lifecycle | Many lifecycle methods | useEffect for all side effects |
| Refs | createRef() |
useRef() |
| Code size | Verbose | Smaller & simpler |
| Logic sharing | HOCs & Render Props | Custom Hooks |
| Recommended | ❌ Legacy | ✔ Modern standard |
render()runs first.componentDidMount()runs after the component is added to the DOM.
Yes — but you must wrap it in a conditional to avoid infinite loops.
The return cleanup function inside useEffect.
useEffectruns after the browser paints.useLayoutEffectruns before paint.
- It can run multiple times in async mode.
- React officially deprecated it.
No — React manages them internally.
React uses a virtual DOM and a diffing algorithm to update the UI efficiently.
- React keeps an in-memory copy of the UI.
- When state changes → React recalculates a new Virtual DOM.
- Then compares old vs new.
Rules:
- If an element type changes (e.g.
<div>→<span>) → React removes and re-renders. - If children change, React compares them in order.
- Keys help React match elements.
Example:
items.map((item, index) => <li key={index}>{item.name}</li>);items.map(item => <li key={item.id}>{item.name}</li>);React rebuilds work in small units:
- Allows rendering to be paused, resumed, or aborted.
- Enables concurrency features like
useTransition.
Without concurrency:
- Large UI updates block the main thread.
With concurrency:
- React spreads work over frames.
- UI stays responsive.
Example:
const [isPending, startTransition] = useTransition();
startTransition(() => {
setQuery(value);
});|---|
| componentDidMount | useEffect(() => {...}, []) |
| componentDidUpdate | useEffect(..., [deps]) |
| componentWillUnmount | cleanup inside useEffect |
Share data across components without prop drilling.
const ThemeContext = React.createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function ThemeButton() {
const theme = useContext(ThemeContext);
return <button className={theme}>Theme: {theme}</button>;
}const inputRef = useRef(null);
<input ref={inputRef} />ReactDOM.createPortal(<Modal />, document.body);class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) return <h1>Error occurred</h1>;
return this.props.children;
}
}useEffect(() => {
fetch('/api/users')
.then(r => r.json())
.then(setUsers);
}, []);Use libraries like React Query or SWR for caching, refetching, etc.
Allow server-side data fetching and reduce client JavaScript.
const LazyComp = React.lazy(() => import('./Big'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComp />
</Suspense>
);
}const [isPending, startTransition] = useTransition();
startTransition(() => setQuery(value));Popular choices:
react-router- Next.js
- Remix
Example:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<Users />} />
</Routes>
</BrowserRouter>
);
}Modern setups:
- Vite (recommended)
- Webpack
- Turbopack
- Rollup
CRA is no longer recommended for new apps.
- Profile with React DevTools
- Use
React.memofor memoizing components - Use
useMemo/useCallbackwhen needed - Virtualize large lists
- Avoid premature optimization
- Prefer semantic HTML
- Provide screen-reader labels
- Manage focus (especially in modals)
- Small, reusable components
- Lift state when shared
- Derive UI from state
- Use custom hooks to share logic
- Excessive prop drilling
- Overusing
useMemoanduseCallback - Storing huge objects in state unnecessarily
- Convert
render()→ return JSX - Replace
this.statewith hooks - Convert lifecycle methods to
useEffect
function TodoApp() {
const [todos, setTodos] = useState([]);
const [text, setText] = useState('');
function add(e) {
e.preventDefault();
setTodos(t => [...t, { id: Date.now(), text }]);
setText('');
}
return (
<form onSubmit={add}>
<input value={text} onChange={e => setText(e.target.value)} />
<button>Add</button>
{todos.map(todo => <li key={todo.id}>{todo.text}</li>)}
</form>
);
}my-app/
├─ index.html
├─ src/
│ ├─ main.jsx
│ ├─ App.jsx
│ ├─ components/
│ ├─ hooks/
│ ├─ pages/
│ └─ styles/
└─ package.json
This documentation provides a practical overview of modern React development. You can:
- Use it directly in a GitHub repository
- Extend it with examples
- Turn it into a reference for future learning
Happy coding!