π What are Hooks?
Hooks are special functions in React that let you use state and lifecycle features inside functional components.
Introduced in React 16.8.
They removed the need to write class components for most use cases.
β Why were Hooks introduced?
Before Hooks:
State and lifecycle methods worked only in class components.
Class components had problems:
Confusing
thiskeywordManual binding of event handlers
Large and complex lifecycle methods
Hard to reuse logic between components
Hooks solved these issues by allowing:
State in functional components
Cleaner and shorter code
Better logic reuse
Easier learning and maintenance
βοΈ useState Hook
useStatelets you add state to a functional component.It returns:
Current state value
Function to update the state
Syntax:
const [state, setState] = useState(initialValue);π§ͺ Example using useState
<div class="infographic-execflow" id="ef">
<style>
.infographic-execflow {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-deep: #1942a8;
--accent-soft: #e3ebfd;
--line: #ddd6c9;
--radius: 8px;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', system-ui, sans-serif;
--mono-font: 'IBM Plex Mono', monospace;
max-width: 880px;
margin: 0 auto;
box-sizing: border-box;
background: transparent;
color: var(--ink);
font-family: var(--body-font);
}
.infographic-execflow[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #13171f;
--paper-alt: #1c212b;
--accent: #5b8def;
--accent-deep: #8fb4ff;
--accent-soft: #20293f;
--line: #2c313d;
}
.infographic-execflow * { box-sizing: border-box; min-width: 0; }
.ef-eyebrow {
font-family: var(--mono-font); font-size: 0.72rem; letter-spacing: 0.1em;
text-transform: uppercase; color: var(--accent); margin: 0 0 0.45rem;
display: flex; align-items: center; justify-content: center; gap: 0.4rem;
}
.ef-eyebrow::before { content: ""; width: 14px; height: 2px; background: var(--accent); display: inline-block; }
.ef-title {
font-family: var(--display-font); font-size: clamp(1.5rem, 4vw, 2.3rem); text-align: center;
margin: 0 0 0.5rem; line-height: 1.12; letter-spacing: -0.01em;
}
.ef-sub { color: var(--ink-soft); font-size: 0.95rem; text-align: center; margin: 0 0 1.9rem; }
.ef-grid { display: grid; grid-template-columns: 1.3fr 1fr; gap: 1.5rem; align-items: start; margin-bottom: 1.3rem; }
@media (max-width: 720px) { .ef-grid { grid-template-columns: 1fr; } }
/* ===== code panel ===== */
.ef-code-head { display: flex; align-items: center; justify-content: center; gap: 0.4rem; margin-bottom: 0.5rem; }
.ef-code-head svg { width: 15px; height: 15px; flex-shrink: 0; }
.ef-code-name { font-size: 0.8rem; font-weight: 600; color: var(--ink); }
.ef-code-block pre {
margin: 0; background: #0d0d10; border: 1px solid #24252d; border-radius: var(--radius);
padding: 0.9rem 0.6rem; overflow-x: auto;
}
.ef-code-block code { font-family: var(--mono-font); font-size: 0.74rem; line-height: 1.75; color: #d4d4d8; white-space: pre; }
.ef-line {
display: block; padding: 0 0.6rem; margin: 0 -0.6rem; border-left: 2px solid transparent;
border-radius: 3px; transition: background 0.25s ease, border-color 0.25s ease;
}
.ef-line.is-current { background: rgba(37,99,235,0.2); border-left-color: var(--accent); }
.tok-kw { color: #c678dd; }
.tok-tag { color: #e06c75; }
.tok-type { color: #61afef; }
.tok-attr { color: #d19a66; }
.tok-str { color: #98c379; }
.tok-expr { color: #56b6c2; }
.tok-punc { color: #6b7280; }
/* ===== output panel ===== */
.ef-output-wrap { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; }
.ef-output-label {
font-family: var(--mono-font); font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-soft);
}
.ef-preview {
border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; width: 100%; max-width: 260px;
box-shadow: 0 10px 24px -12px rgba(26,26,26,0.18);
}
.ef-preview-bar { background: #e9e9e9; padding: 0.45rem 0.6rem; display: flex; gap: 0.3rem; }
.ef-preview-bar span { width: 8px; height: 8px; border-radius: 50%; display: block; }
.ef-preview-bar span:nth-child(1) { background: #ff5f56; }
.ef-preview-bar span:nth-child(2) { background: #ffbd2e; }
.ef-preview-bar span:nth-child(3) { background: #27c93f; }
.ef-preview-page { background: #fff; padding: 1.6rem 1.2rem; text-align: center; font-family: Times, "Times New Roman", serif; }
.ef-preview-page p { margin: 0 0 1rem; font-size: 1.05rem; color: #000; }
.ef-preview-btn {
position: relative; font-family: var(--body-font); font-size: 0.85rem; font-weight: 600; color: #000;
background: #efefef; border: 2px outset #ccc; padding: 0.4rem 0.9rem; cursor: pointer;
}
.ef-preview-btn:disabled { color: #999; cursor: default; opacity: 0.7; }
.ef-click-ping {
position: absolute; inset: -8px; border: 2px solid var(--accent); border-radius: 10px;
opacity: 0; pointer-events: none;
}
.ef-preview-page.is-clicking .ef-click-ping { animation: ef-ping 0.6s ease-out; }
@keyframes ef-ping { 0% { opacity: 0.9; transform: scale(0.92); } 100% { opacity: 0; transform: scale(1.25); } }
.ef-hint { font-size: 0.72rem; color: var(--ink-soft); text-align: center; min-height: 1.4em; }
/* ===== progress + caption ===== */
.ef-caption {
text-align: center; font-size: 0.92rem; color: var(--ink); min-height: 2.6em; display: flex;
align-items: center; justify-content: center; max-width: 560px; margin: 0 auto 0.9rem; line-height: 1.45;
padding: 0 0.5rem;
}
.ef-dots { display: flex; justify-content: center; gap: 0.4rem; }
.ef-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--line); transition: background 0.2s, transform 0.2s; }
.ef-dot.is-current { background: var(--accent); transform: scale(1.3); }
.ef-dot.is-done { background: var(--accent-deep); }
.ef-takeaway {
margin-top: 1.7rem; padding: 0.95rem 1.2rem; background: var(--accent-soft);
border-left: 3px solid var(--accent); border-radius: 4px; font-size: 0.9rem; color: var(--ink); line-height: 1.5;
max-width: 620px; margin-left: auto; margin-right: auto;
}
.ef-takeaway b { color: var(--accent-deep); }
@media (prefers-reduced-motion: reduce) {
.infographic-execflow *, .infographic-execflow *::before, .infographic-execflow *::after {
transition-duration: 0.001ms !important; animation-duration: 0.001ms !important;
}
}
</style>
<div class="ef-eyebrow">Execution Flow</div>
<h1 class="ef-title">What Happens on Click</h1>
<p class="ef-sub">Click Increment in the output to watch it run.</p>
<div class="ef-grid">
<div class="ef-code-block">
<div class="ef-code-head">
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M6 3 L2 8 L6 13" fill="none" stroke="var(--accent)" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 3 L14 8 L10 13" fill="none" stroke="var(--accent)" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
<span class="ef-code-name">Counter.jsx</span>
</div>
<pre><code><span class="ef-line" data-line="1"><span class="tok-kw">import</span> <span class="tok-type">React</span><span class="tok-punc">, {</span> <span class="tok-type">useState</span> <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">"react"</span><span class="tok-punc">;</span></span><span class="ef-line" data-line="2"><span class="tok-kw">function</span> <span class="tok-type">Counter</span><span class="tok-punc">() {</span></span><span class="ef-line" data-line="3"> <span class="tok-kw">const</span> <span class="tok-punc">[</span>count<span class="tok-punc">,</span> setCount<span class="tok-punc">]</span> <span class="tok-punc">=</span> <span class="tok-type">useState</span><span class="tok-punc">(</span><span class="tok-expr">0</span><span class="tok-punc">);</span></span><span class="ef-line" data-line="4"> <span class="tok-kw">return</span> <span class="tok-punc">(</span></span><span class="ef-line" data-line="5"> <span class="tok-punc"><</span><span class="tok-tag">div</span><span class="tok-punc">></span></span><span class="ef-line" data-line="6"> <span class="tok-punc"><</span><span class="tok-tag">p</span><span class="tok-punc">></span>Count: <span class="tok-punc">{</span><span class="tok-expr">count</span><span class="tok-punc">}</span><span class="tok-punc"></</span><span class="tok-tag">p</span><span class="tok-punc">></span></span><span class="ef-line" data-line="7"> <span class="tok-punc"><</span><span class="tok-tag">button</span> <span class="tok-attr">onClick</span><span class="tok-punc">={() =></span> setCount<span class="tok-punc">(</span>count <span class="tok-punc">+</span> <span class="tok-expr">1</span><span class="tok-punc">)}></span></span><span class="ef-line" data-line="8"> Increment</span><span class="ef-line" data-line="9"> <span class="tok-punc"></</span><span class="tok-tag">button</span><span class="tok-punc">></span></span><span class="ef-line" data-line="10"> <span class="tok-punc"></</span><span class="tok-tag">div</span><span class="tok-punc">></span></span><span class="ef-line" data-line="11"> <span class="tok-punc">);</span></span><span class="ef-line" data-line="12"><span class="tok-punc">}</span></span><span class="ef-line" data-line="13"><span class="tok-kw">export</span> <span class="tok-kw">default</span> <span class="tok-type">Counter</span><span class="tok-punc">;</span></span></code></pre>
</div>
<div class="ef-output-wrap">
<div class="ef-output-label">Output</div>
<div class="ef-preview">
<div class="ef-preview-bar"><span></span><span></span><span></span></div>
<div class="ef-preview-page" id="efPreviewPage">
<p id="efCount">Count: 0</p>
<button class="ef-preview-btn" id="efIncBtn" type="button">Increment<span class="ef-click-ping"></span></button>
</div>
</div>
<div class="ef-hint" id="efHint">Click the button to run the code</div>
</div>
</div>
<p class="ef-caption" id="efCaption">Click Increment to see what runs.</p>
<div class="ef-dots" id="efDots"></div>
<div class="ef-takeaway"><b>A click doesn't patch the number</b> β it re-runs the whole component, and <code style="font-family:var(--mono-font)">useState</code> hands back the value it remembered.</div>
</div>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:wght@500;600&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<script>
(function () {
var root = document.getElementById('ef');
if (!root) return;
/* --- dark mode detection: body attribute > cookie > postMessage > light --- */
function applyDark(dark) {
if (dark) root.setAttribute('data-theme', 'dark');
else root.removeAttribute('data-theme');
}
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') dark = true;
else if (bodyAttr === 'false') dark = false;
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
applyDark(dark);
window.addEventListener('message', function (e) {
if (e.data && e.data.type === 'darkMode') applyDark(!!e.data.value);
});
/* --- click-driven execution playback --- */
var lineEls = root.querySelectorAll('.ef-line');
var caption = root.querySelector('#efCaption');
var countEl = root.querySelector('#efCount');
var previewPage = root.querySelector('#efPreviewPage');
var btn = root.querySelector('#efIncBtn');
var hint = root.querySelector('#efHint');
var dotsWrap = root.querySelector('#efDots');
var STEP_DELAY = 900;
var count = 0;
var playing = false;
function buildSteps(prev, next) {
return [
{ lines: [2], caption: 'Counter() runs.', display: prev, click: false },
{ lines: [3], caption: 'useState returns the current count: ' + prev + '.', display: prev, click: false },
{ lines: [6], caption: 'JSX renders \u2014 shows Count: ' + prev + '.', display: prev, click: false },
{ lines: [7], caption: 'Click detected \u2192 setCount(' + prev + ' + 1) runs.', display: prev, click: true },
{ lines: [2], caption: 'State changed \u2014 Counter() runs again.', display: prev, click: false },
{ lines: [3], caption: 'useState now returns ' + next + '.', display: prev, click: false },
{ lines: [6], caption: 'New JSX renders \u2014 Count: ' + next + '.', display: next, click: false }
];
}
var dots = [];
for (var i = 0; i < 7; i++) {
var d = document.createElement('span');
d.className = 'ef-dot';
dotsWrap.appendChild(d);
dots.push(d);
}
function showStep(steps, index) {
var step = steps[index];
lineEls.forEach(function (el) {
var n = parseInt(el.getAttribute('data-line'), 10);
el.classList.toggle('is-current', step.lines.indexOf(n) !== -1);
});
caption.textContent = step.caption;
countEl.textContent = 'Count: ' + step.display;
dots.forEach(function (d, i) {
d.classList.toggle('is-current', i === index);
d.classList.toggle('is-done', i < index);
});
previewPage.classList.remove('is-clicking');
if (step.click) {
void previewPage.offsetWidth;
previewPage.classList.add('is-clicking');
}
}
function playFlow() {
if (playing) return;
playing = true;
btn.disabled = true;
hint.textContent = 'Running\u2026';
var prev = count;
var next = count + 1;
var steps = buildSteps(prev, next);
var i = 0;
showStep(steps, 0);
var timer = setInterval(function () {
i++;
if (i >= steps.length) {
clearInterval(timer);
count = next;
playing = false;
btn.disabled = false;
hint.textContent = 'Click the button to run the code';
dots.forEach(function (d) { d.classList.remove('is-current', 'is-done'); });
return;
}
showStep(steps, i);
}, STEP_DELAY);
}
btn.addEventListener('click', playFlow);
})();
</script>π§ How it works
useState(0)β initializes state with0countβ current valuesetCountβ updates stateUpdating state re-renders the component
βοΈ Comparison: Hooks vs Class Components
Feature | Class Component | Hooks (Functional Component) |
|---|---|---|
Component type | Class-based | Function-based |
State handling |
|
|
State update |
|
|
| Required | β Not used |
Event binding | Required | β Not needed |
Lifecycle methods |
|
|
Code length | More boilerplate | Cleaner & shorter |
Logic reuse | Hard (HOCs, render props) | Easy (custom hooks) |
Learning curve | Steeper | Easier |
Modern React | β Less preferred | β Recommended |
π Key Rules of Hooks
Use Hooks only in functional components
Call Hooks at the top level
Do not call Hooks inside loops or conditions