⚠️ useEffect with Incorrect Dependency
The dependency array tells React when to re-run an effect.
If dependencies are missing or incorrect, your effect may:
Use stale (old) values
Not run when it should
Run too many times
Cause bugs or infinite loops
❌ Missing Dependency Example
useEffect(() => {
console.log(count);
}, []); // ❌ count is missing🔴 Problem:
Effect runs only once
countvalue never updates inside effect
✅ Correct Version
useEffect(() => {
console.log(count);
}, [count]);✔️ Effect runs whenever count changes
❌ Too Many Dependencies
useEffect(() => {
fetchData();
}, [count]); // ❌ fetchData doesn't depend on count🔴 Problem:
Unnecessary re-runs
❌ Infinite Loop Example
useEffect(() => {
setCount(count + 1);
}, [count]); // ❌ infinite updates<div class="infographic-uedeps" id="ud">
<style>
.infographic-uedeps {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--line: #ddd6c9;
--accent: #2563eb;
--bad: #dc2626;
--good: #16a34a;
--radius: 8px;
--mono-font: 'IBM Plex Mono', monospace;
--body-font: 'Inter', system-ui, sans-serif;
max-width: 720px;
margin: 0 auto;
box-sizing: border-box;
background: transparent;
color: var(--ink);
font-family: var(--body-font);
}
.infographic-uedeps[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--line: #2c313d;
--accent: #5b8def;
}
.infographic-uedeps * { box-sizing: border-box; min-width: 0; }
.ud-tabs { display: flex; justify-content: center; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1.1rem; }
.ud-tab {
font: inherit; font-size: 0.78rem; font-weight: 600; color: var(--ink-soft); background: transparent;
border: 1px solid var(--line); border-radius: 100px; padding: 0.45rem 0.9rem; cursor: pointer;
transition: background 0.2s, color 0.2s, border-color 0.2s;
}
.ud-tab.is-active { background: var(--accent); border-color: var(--accent); color: #fff; }
.ud-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ud-grid { display: flex; flex-direction: column; gap: 1.1rem; align-items: stretch; }
.ud-code-block pre {
margin: 0; background: #0d0d10; border: 1px solid #24252d; border-radius: var(--radius);
padding: 0.9rem; overflow: auto; max-height: 260px;
}
.ud-code-block code { font-family: var(--mono-font); font-size: 0.78rem; line-height: 1.75; color: #d4d4d8; white-space: pre; }
.tok-kw { color: #c678dd; }
.tok-punc { color: #6b7280; }
.tok-str { color: #98c379; }
.tok-expr { color: #56b6c2; }
.tok-comment { color: #7f848e; font-style: italic; }
.ud-output-label {
font-family: var(--mono-font); font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase;
color: var(--ink-soft); text-align: center;
}
.ud-panel { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: 0 10px 24px -12px rgba(26,26,26,0.18); background: #fff; }
.ud-panel-bar { background: #e9e9e9; padding: 0.45rem 0.6rem; display: flex; gap: 0.3rem; }
.ud-panel-bar span { width: 8px; height: 8px; border-radius: 50%; display: block; }
.ud-panel-bar span:nth-child(1) { background: #ff5f56; }
.ud-panel-bar span:nth-child(2) { background: #ffbd2e; }
.ud-panel-bar span:nth-child(3) { background: #27c93f; }
.ud-panel-body { padding: 1.3rem 1.4rem; text-align: center; }
.ud-real-btn {
font-family: var(--body-font); font-size: 0.85rem; font-weight: 600; color: #fff; background: var(--accent);
border: none; border-radius: 100px; padding: 0.55rem 1.1rem; cursor: pointer; margin: 0 0.3rem;
transition: transform 0.15s;
}
.ud-real-btn:hover:not(:disabled) { transform: translateY(-1px); }
.ud-real-btn:disabled { opacity: 0.5; cursor: default; }
.ud-count-display { font-family: var(--mono-font); font-size: 1.3rem; color: #000; margin-bottom: 0.9rem; }
.ud-console { background: #0d0d10; border-radius: 6px; padding: 0.6rem 0.8rem; text-align: left; margin-top: 1rem; max-height: 130px; overflow-y: auto; }
.ud-line { font-family: var(--mono-font); font-size: 0.74rem; line-height: 1.7; color: #98c379; opacity: 0; animation: ud-fade 0.3s ease forwards; }
.ud-line.ud-bad { color: #e06c75; }
.ud-line.ud-warn { color: #e5c07b; font-style: italic; }
@keyframes ud-fade { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
.infographic-uedeps *, .infographic-uedeps *::before, .infographic-uedeps *::after {
transition-duration: 0.001ms !important; animation-duration: 0.001ms !important;
}
}
</style>
<div class="ud-tabs">
<button class="ud-tab is-active" data-scenario="missing">❌ Missing dep</button>
<button class="ud-tab" data-scenario="correct">✅ Correct</button>
<button class="ud-tab" data-scenario="extra">❌ Extra dep</button>
<button class="ud-tab" data-scenario="loop">❌ Infinite loop</button>
</div>
<div class="ud-grid">
<div class="ud-code-block"><pre><code id="udCode"></code></pre></div>
<p class="ud-output-label">OUTPUT</p>
<div class="ud-panel">
<div class="ud-panel-bar"><span></span><span></span><span></span></div>
<div class="ud-panel-body" id="udPanelBody"></div>
</div>
</div>
</div>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<script>
(function () {
var root = document.getElementById('ud');
if (!root) return;
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;
}
if (dark) root.setAttribute('data-theme', 'dark');
window.addEventListener('message', function (e) {
if (e.data && e.data.type === 'darkMode') {
if (e.data.value) root.setAttribute('data-theme', 'dark');
else root.removeAttribute('data-theme');
}
});
var codeEl = root.querySelector('#udCode');
var panelBody = root.querySelector('#udPanelBody');
var tabs = Array.prototype.slice.call(root.querySelectorAll('.ud-tab'));
var activeTimers = [];
function clearTimers() {
activeTimers.forEach(function (t) { clearInterval(t); clearTimeout(t); });
activeTimers = [];
}
function logLine(container, text, cls) {
var div = document.createElement('div');
div.className = 'ud-line' + (cls ? ' ' + cls : '');
div.textContent = text;
container.appendChild(div);
container.scrollTop = container.scrollHeight;
}
var scenarios = {
missing: {
code:
'useEffect<span class="tok-punc">(() => {</span>\n' +
' console.log<span class="tok-punc">(</span>count<span class="tok-punc">);</span>\n' +
'<span class="tok-punc">},</span> <span class="tok-punc">[]);</span> <span class="tok-comment">// ❌ count is missing</span>',
build: function () {
panelBody.innerHTML =
'<div class="ud-count-display">count: <span id="udVal">0</span></div>' +
'<button class="ud-real-btn" id="udIncBtn" type="button">Increment</button>' +
'<div class="ud-console" id="udConsole"></div>';
var val = 0;
var valEl = root.querySelector('#udVal');
var console_ = root.querySelector('#udConsole');
logLine(console_, 'Effect ran → logged count: 0 (on mount)');
root.querySelector('#udIncBtn').addEventListener('click', function () {
val += 1;
valEl.textContent = val;
// effect has [] deps — it never runs again, so no new log appears
});
}
},
correct: {
code:
'useEffect<span class="tok-punc">(() => {</span>\n' +
' console.log<span class="tok-punc">(</span>count<span class="tok-punc">);</span>\n' +
'<span class="tok-punc">},</span> <span class="tok-punc">[count]);</span>',
build: function () {
panelBody.innerHTML =
'<div class="ud-count-display">count: <span id="udVal">0</span></div>' +
'<button class="ud-real-btn" id="udIncBtn" type="button">Increment</button>' +
'<div class="ud-console" id="udConsole"></div>';
var val = 0;
var valEl = root.querySelector('#udVal');
var console_ = root.querySelector('#udConsole');
logLine(console_, 'Effect ran → logged count: 0 (on mount)');
root.querySelector('#udIncBtn').addEventListener('click', function () {
val += 1;
valEl.textContent = val;
logLine(console_, 'Effect ran → logged count: ' + val);
});
}
},
extra: {
code:
'useEffect<span class="tok-punc">(() => {</span>\n' +
' fetchData<span class="tok-punc">();</span>\n' +
'<span class="tok-punc">},</span> <span class="tok-punc">[count]);</span> <span class="tok-comment">// ❌ fetchData doesn\'t use count</span>',
build: function () {
panelBody.innerHTML =
'<div class="ud-count-display">count (unrelated to fetch): <span id="udVal">0</span></div>' +
'<button class="ud-real-btn" id="udIncBtn" type="button">Increment count</button>' +
'<div class="ud-console" id="udConsole"></div>';
var val = 0;
var valEl = root.querySelector('#udVal');
var console_ = root.querySelector('#udConsole');
logLine(console_, 'fetchData() called (on mount)');
root.querySelector('#udIncBtn').addEventListener('click', function () {
val += 1;
valEl.textContent = val;
logLine(console_, 'fetchData() called again — but count has nothing to do with the fetch', 'ud-warn');
});
}
},
loop: {
code:
'useEffect<span class="tok-punc">(() => {</span>\n' +
' setCount<span class="tok-punc">(</span>count <span class="tok-punc">+</span> <span class="tok-expr">1</span><span class="tok-punc">);</span>\n' +
'<span class="tok-punc">},</span> <span class="tok-punc">[count]);</span> <span class="tok-comment">// ❌ infinite updates</span>',
build: function () {
panelBody.innerHTML =
'<div class="ud-count-display">count: <span id="udVal">0</span></div>' +
'<button class="ud-real-btn" id="udRunBtn" type="button">▶ Run effect</button>' +
'<div class="ud-console" id="udConsole"></div>';
var val = 0;
var valEl = root.querySelector('#udVal');
var console_ = root.querySelector('#udConsole');
var runBtn = root.querySelector('#udRunBtn');
runBtn.addEventListener('click', function () {
runBtn.disabled = true;
var n = 0;
var t = setInterval(function () {
val += 1;
n += 1;
valEl.textContent = val;
if (n <= 6 || n % 5 === 0) logLine(console_, 'setCount(' + val + ') → effect fires again...');
if (n >= 40) {
clearInterval(t);
logLine(console_, '⚠️ stopped after 40 renders — real code would freeze the tab forever', 'ud-bad');
runBtn.textContent = '🔥 Would loop forever';
}
}, 40);
activeTimers.push(t);
});
}
}
};
function showScenario(key) {
clearTimers();
codeEl.innerHTML = scenarios[key].code;
scenarios[key].build();
tabs.forEach(function (t) { t.classList.toggle('is-active', t.getAttribute('data-scenario') === key); });
}
tabs.forEach(function (t) {
t.addEventListener('click', function () { showScenario(t.getAttribute('data-scenario')); });
});
showScenario('missing');
})();
</script>🧠 Rule of Thumb
Every state or prop used inside useEffect must be listed in the dependency array.
🔁 Class Component Comparison
Class Component | useEffect |
|---|---|
| Dependency array |
🔚 In Short
Missing dependency → stale data
Extra dependency → unnecessary runs
Wrong dependency → bugs or loops
✔️ Always keep dependency array accurate