When your application grows, a single "Global Context" becomes a performance bottleneck and a maintenance nightmare. Handling multiple contexts effectively is about modularity and separation of concerns.
❓ Why Use Multiple Contexts?
Performance: React re-renders all consumers of a context when its value changes. By splitting context, a change in
Themewon't force a re-render of theAuthlogic.Maintainability: Keeping
CartContextseparate fromUserContextmakes the code easier to read and debug.Reusability: You can move specific contexts (like a
ThemeContext) between projects easily.
🔴 The Context Composition Pattern
The standard way to handle multiple contexts is to "nest" the providers. However, this can lead to the "Provider Wrapper Hell."
The "Nested" Approach (Standard)
function App() {
return (
<AuthProvider>
<ThemeProvider>
<CartProvider>
<MainLayout />
</CartProvider>
</ThemeProvider>
</AuthProvider>
);
}✅ Solving "Wrapper Hell" with a Provider Composer
If you have 5+ contexts, your App.js becomes hard to read. You can create a utility component to flatten the structure.
Create a Composer Component
const ContextComposer = ({ providers, children }) => {
return (
<>
{providers.reduceRight((acc, Provider) => {
return <Provider>{acc}</Provider>;
}, children)}
</>
);
};Usage in App.js
function App() {
return (
<ContextComposer providers={[AuthProvider, ThemeProvider, CartProvider]}>
<MainLayout />
</ContextComposer>
);
}🧠 Consuming Multiple Contexts
In functional components, consuming multiple contexts is clean and straightforward using hooks.
function Header() {
// You can call as many hooks as you need
const { user } = useAuth();
const { theme } = useTheme();
const { itemCount } = useCart();
return (
<header className={theme}>
<p>Hello, {user.name}. You have {itemCount} items in your cart.</p>
</header>
);
}🚀 Performance Checklist
When handling multiple contexts, keep these rules in mind to prevent lag:
Split by Frequency of Change: Don't put "Static Data" (Company Info) in the same context as "Volatile Data" (Real-time stock prices).
Memoize Values: Use
useMemofor the context value to prevent unnecessary re-renders when the parent component re-renders.const value = useMemo(() => ({ user, login, logout }), [user]);Specific Consumers: Ensure components only subscribe to the context they actually need.
<div class="infographic-vscodeterm" id="vt">
<style>
.infographic-vscodeterm {
--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: 900px;
margin: 0 auto;
box-sizing: border-box;
background: transparent;
color: var(--ink);
font-family: var(--body-font);
}
.infographic-vscodeterm[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #13171f;
--paper-alt: #1c212b;
--accent: #5b8def;
--accent-deep: #8fb4ff;
--accent-soft: #20293f;
--line: #2c313d;
}
.infographic-vscodeterm * { box-sizing: border-box; min-width: 0; }
.vt-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;
}
.vt-eyebrow::before { content: ""; width: 14px; height: 2px; background: var(--accent); display: inline-block; }
.vt-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.14; letter-spacing: -0.01em;
}
.vt-sub { color: var(--ink-soft); font-size: 0.95rem; text-align: center; margin: 0 0 1.7rem; }
/* ===== VS Code window — fixed dark theme regardless of page mode ===== */
.vt-window { border-radius: 8px; overflow: hidden; box-shadow: 0 16px 40px -14px rgba(0,0,0,0.45); border: 1px solid #1e1e1e; font-family: var(--mono-font); }
.vt-titlebar { background: #323233; display: flex; align-items: center; padding: 0.5rem 0.8rem; position: relative; }
.vt-dots { display: flex; gap: 0.4rem; }
.vt-dots span { width: 10px; height: 10px; border-radius: 50%; display: block; }
.vt-dots span:nth-child(1) { background: #ff5f56; }
.vt-dots span:nth-child(2) { background: #ffbd2e; }
.vt-dots span:nth-child(3) { background: #27c93f; }
.vt-titlebar-text { position: absolute; left: 50%; transform: translateX(-50%); font-size: 0.72rem; color: #cccccc; }
.vt-body { display: flex; height: 340px; }
.vt-resize-h { width: 5px; cursor: col-resize; background: transparent; flex-shrink: 0; transition: background 0.15s; touch-action: none; }
.vt-resize-h:hover, .vt-resize-h.is-dragging { background: #0078d4; }
.vt-resize-v { height: 5px; cursor: row-resize; background: transparent; flex-shrink: 0; transition: background 0.15s; touch-action: none; }
.vt-resize-v:hover, .vt-resize-v.is-dragging { background: #0078d4; }
.vt-window-resize {
height: 9px; background: #252526; cursor: row-resize; display: flex; align-items: center;
justify-content: center; border-top: 1px solid #1e1e1e; touch-action: none;
}
.vt-window-resize span { width: 30px; height: 3px; border-radius: 2px; background: #4a4a52; }
.vt-window-resize:hover span, .vt-window-resize.is-dragging span { background: #0078d4; }
.vt-toggle-btn {
background: none; border: none; color: #9a9a9a; cursor: pointer; display: flex; align-items: center;
padding: 0.3rem; border-radius: 3px; flex-shrink: 0;
}
.vt-toggle-btn:hover { background: #2a2d2e; color: #fff; }
.vt-toggle-btn svg { width: 13px; height: 13px; }
.vt-sidebar.is-hidden { width: 0 !important; overflow: hidden; border-right: none; }
.vt-terminal.is-collapsed { height: auto !important; }
.vt-terminal.is-collapsed .vt-term-body { display: none; }
.vt-terminal.is-collapsed .vt-term-toggle svg { transform: rotate(-90deg); }
.vt-activitybar { width: 40px; background: #333333; display: flex; flex-direction: column; align-items: center; padding-top: 0.7rem; gap: 1.1rem; flex-shrink: 0; }
.vt-activitybar svg { width: 18px; height: 18px; opacity: 0.55; }
.vt-activitybar svg.is-on { opacity: 1; }
.vt-sidebar { width: 190px; background: #252526; flex-shrink: 0; overflow-y: auto; border-right: 1px solid #1e1e1e; }
.vt-sidebar-head { font-size: 0.65rem; font-weight: 700; letter-spacing: 0.06em; color: #bbbbbb; padding: 0.6rem 0.8rem 0.3rem; }
.vt-sidebar-root { font-size: 0.74rem; color: #cccccc; padding: 0.2rem 0.7rem; display: flex; align-items: center; gap: 0.3rem; }
.vt-sidebar-folder { font-size: 0.74rem; color: #cccccc; padding: 0.2rem 0.7rem 0.2rem 1.4rem; display: flex; align-items: center; gap: 0.3rem; }
.vt-row {
display: flex; align-items: center; gap: 0.35rem; width: 100%; background: none; border: none;
color: #cccccc; font: inherit; font-size: 0.72rem; padding: 0.22rem 0.7rem 0.22rem 2.1rem;
cursor: pointer; text-align: left; border-left: 2px solid transparent;
}
.vt-row:hover { background: #2a2d2e; }
.vt-row.is-selected { background: #37373d; border-left-color: #0078d4; }
.vt-badge { width: 15px; height: 15px; border-radius: 3px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 0.46rem; font-weight: 700; color: #1a1a1a; background: #29b6f6; }
.vt-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.vt-tabbar { display: flex; background: #252526; border-bottom: 1px solid #1e1e1e; overflow-x: auto; }
.vt-tab {
display: flex; align-items: center; gap: 0.35rem; background: #1e1e1e; color: #808080; font: inherit;
font-size: 0.7rem; padding: 0.45rem 0.7rem; border: none; border-right: 1px solid #1e1e1e;
border-top: 2px solid transparent; cursor: pointer; white-space: nowrap;
}
.vt-tab.is-active { color: #d4d4d4; border-top-color: #0078d4; background: #1e1e1e; }
.vt-tab .vt-badge { width: 13px; height: 13px; font-size: 0.42rem; }
.vt-code-wrap { flex: 1; background: #1e1e1e; overflow: auto; padding: 0.85rem 1rem; }
.vt-code-wrap pre { margin: 0; }
.vt-code-wrap code { font-family: var(--mono-font); font-size: 0.7rem; line-height: 1.68; color: #d4d4d8; white-space: pre; }
.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; }
.tok-comment { color: #7f848e; font-style: italic; }
/* ===== terminal ===== */
.vt-terminal { background: #181818; border-top: 1px solid #2c2c2c; display: flex; flex-direction: column; height: 150px; flex-shrink: 0; }
.vt-term-head { display: flex; align-items: center; gap: 0.4rem; font-size: 0.62rem; letter-spacing: 0.05em; color: #9a9a9a; padding: 0.35rem 0.8rem; border-bottom: 1px solid #262626; text-transform: uppercase; }
.vt-term-body { flex: 1; overflow-y: auto; padding: 0.5rem 0.9rem; font-size: 0.72rem; line-height: 1.65; }
.vt-term-line { color: #d4d4d4; white-space: pre; opacity: 0; transform: translateY(2px); animation: vt-line-in 0.25s ease forwards; }
@keyframes vt-line-in { to { opacity: 1; transform: none; } }
.vt-term-line.is-cmd { color: #7ee787; }
.vt-term-line.is-ok { color: #7ee787; font-weight: 600; }
.vt-term-prompt { color: #7ee787; }
.vt-run-row { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.2rem; }
.vt-run-btn {
font: inherit; font-size: 0.68rem; font-weight: 600; color: #1a1a1a; background: #7ee787;
border: none; border-radius: 4px; padding: 0.3rem 0.7rem; cursor: pointer; display: flex; align-items: center; gap: 0.3rem;
}
.vt-run-btn:disabled { opacity: 0.5; cursor: default; }
.vt-run-btn svg { width: 9px; height: 9px; }
.vt-run-hint { color: #6b7280; font-size: 0.68rem; }
@media (max-width: 620px) {
.vt-sidebar { width: 150px; }
.vt-body { height: 360px; }
}
@media (max-width: 480px) { .vt-activitybar { display: none; } }
/* ===== browser output ===== */
.vt-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; margin: 1.5rem 0 0.7rem;
}
.vt-browser {
border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; max-width: 420px; margin: 0 auto;
box-shadow: 0 10px 24px -12px rgba(26,26,26,0.18);
opacity: 0; transform: translateY(8px); transition: opacity 0.4s ease, transform 0.4s ease; pointer-events: none;
}
.vt-browser.is-shown { opacity: 1; transform: none; pointer-events: auto; }
.vt-browser-bar { background: #e9e9e9; padding: 0.5rem 0.7rem; display: flex; align-items: center; gap: 0.5rem; }
.vt-browser-bar .vt-dots span { width: 8px; height: 8px; }
.vt-browser-url { font-family: var(--mono-font); font-size: 0.65rem; color: #555; background: #fff; border-radius: 4px; padding: 0.2rem 0.5rem; flex: 1; }
.vt-output-placeholder { text-align: center; font-size: 0.82rem; color: var(--ink-soft); }
/* --- Header --- */
.vt-header { padding: 1.4rem 1.3rem; text-align: center; font-family: var(--body-font); transition: background 0.3s ease, color 0.3s ease; }
.vt-header.theme-light { background: #fff; color: #1a1a1a; }
.vt-header.theme-dark { background: #16171a; color: #f1f1f1; }
.vt-header-render { font-family: var(--mono-font); font-size: 0.62rem; opacity: 0.6; margin: 0 0 0.6rem; }
.vt-header p { margin: 0 0 1rem; font-size: 0.92rem; line-height: 1.5; }
.vt-header-btns { display: flex; justify-content: center; gap: 0.5rem; flex-wrap: wrap; }
.vt-mini-btn {
font-family: var(--body-font); font-size: 0.76rem; font-weight: 600; border: none; border-radius: 100px;
padding: 0.4rem 0.85rem; cursor: pointer; transition: transform 0.15s;
}
.vt-mini-btn:hover { transform: translateY(-1px); }
.vt-mini-btn.btn-cart { background: var(--accent); color: #fff; }
.vt-mini-btn.btn-theme { background: transparent; border: 1px solid currentColor; color: inherit; }
.vt-mini-btn.btn-logout { background: #dc2626; color: #fff; }
.vt-login { padding: 2.2rem 1.4rem; text-align: center; font-family: var(--body-font); background: #fff; }
.vt-login-msg { font-size: 0.92rem; color: #444; margin: 0 0 1rem; }
.vt-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: 660px; margin-left: auto; margin-right: auto;
}
.vt-takeaway b { color: var(--accent-deep); }
.vt-takeaway code { font-family: var(--mono-font); }
@media (prefers-reduced-motion: reduce) {
.infographic-vscodeterm *, .infographic-vscodeterm *::before, .infographic-vscodeterm *::after {
transition-duration: 0.001ms !important; animation-duration: 0.001ms !important;
}
}
</style>
<div class="vt-eyebrow">Context API</div>
<h1 class="vt-title">Composing Multiple Contexts</h1>
<p class="vt-sub">Run the dev server, then change the cart or theme and watch Header react to both.</p>
<div class="vt-window">
<div class="vt-titlebar">
<div class="vt-dots"><span></span><span></span><span></span></div>
<div class="vt-titlebar-text">multi-context-demo — Visual Studio Code</div>
</div>
<div class="vt-body">
<div class="vt-activitybar">
<svg class="is-on" viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="3" y="4" width="7" height="7" rx="1" stroke="#fff" stroke-width="1.6"/><rect x="14" y="4" width="7" height="7" rx="1" stroke="#fff" stroke-width="1.6"/><rect x="3" y="15" width="7" height="7" rx="1" stroke="#fff" stroke-width="1.6"/><rect x="14" y="15" width="7" height="7" rx="1" stroke="#fff" stroke-width="1.6"/></svg>
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><circle cx="10" cy="10" r="6.5" stroke="#fff" stroke-width="1.6"/><line x1="15" y1="15" x2="20" y2="20" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg>
</div>
<div class="vt-sidebar">
<div class="vt-sidebar-head">EXPLORER</div>
<div class="vt-sidebar-root">▾ MULTI-CONTEXT-DEMO</div>
<div class="vt-sidebar-folder">▾ src</div>
<button class="vt-row" data-file="composer" type="button"><span class="vt-badge">JS</span><span>ContextComposer.js</span></button>
<button class="vt-row" data-file="authcontext" type="button"><span class="vt-badge">JS</span><span>AuthContext.js</span></button>
<button class="vt-row" data-file="themecontext" type="button"><span class="vt-badge">JS</span><span>ThemeContext.js</span></button>
<button class="vt-row" data-file="cartcontext" type="button"><span class="vt-badge">JS</span><span>CartContext.js</span></button>
<button class="vt-row is-selected" data-file="app" type="button"><span class="vt-badge">JS</span><span>App.js</span></button>
<button class="vt-row" data-file="appcontent" type="button"><span class="vt-badge">JS</span><span>AppContent.js</span></button>
<button class="vt-row" data-file="loginpage" type="button"><span class="vt-badge">JS</span><span>LoginPage.js</span></button>
<button class="vt-row" data-file="header" type="button"><span class="vt-badge">JS</span><span>Header.js</span></button>
</div>
<div class="vt-resize-h" id="vtSidebarHandle" title="Drag to resize sidebar"></div>
<div class="vt-main">
<div class="vt-tabbar">
<button class="vt-toggle-btn" id="vtToggleSidebar" type="button" aria-label="Toggle sidebar" title="Toggle sidebar">
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><rect x="1.5" y="2" width="13" height="12" rx="1.5" stroke="currentColor" stroke-width="1.3"/><line x1="6" y1="2" x2="6" y2="14" stroke="currentColor" stroke-width="1.3"/></svg>
</button>
<button class="vt-tab" data-file="composer" type="button"><span class="vt-badge">JS</span><span>Composer.js</span></button>
<button class="vt-tab" data-file="authcontext" type="button"><span class="vt-badge">JS</span><span>AuthContext.js</span></button>
<button class="vt-tab" data-file="themecontext" type="button"><span class="vt-badge">JS</span><span>ThemeContext.js</span></button>
<button class="vt-tab" data-file="cartcontext" type="button"><span class="vt-badge">JS</span><span>CartContext.js</span></button>
<button class="vt-tab is-active" data-file="app" type="button"><span class="vt-badge">JS</span><span>App.js</span></button>
<button class="vt-tab" data-file="appcontent" type="button"><span class="vt-badge">JS</span><span>AppContent.js</span></button>
<button class="vt-tab" data-file="loginpage" type="button"><span class="vt-badge">JS</span><span>LoginPage.js</span></button>
<button class="vt-tab" data-file="header" type="button"><span class="vt-badge">JS</span><span>Header.js</span></button>
</div>
<div class="vt-code-wrap">
<pre><code id="vtCode"></code></pre>
</div>
<div class="vt-resize-v" id="vtTerminalHandle" title="Drag to resize terminal"></div>
<div class="vt-terminal">
<div class="vt-term-head">
<button class="vt-toggle-btn vt-term-toggle" id="vtToggleTerminal" type="button" aria-label="Toggle terminal" title="Toggle terminal">
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<span>Terminal</span>
</div>
<div class="vt-term-body">
<div id="vtTermLines"><div class="vt-term-line" style="animation-delay:0s;"><span class="vt-term-prompt">$</span> </div></div>
<div class="vt-run-row">
<button class="vt-run-btn" id="vtRunBtn" type="button">
<svg viewBox="0 0 12 12" aria-hidden="true"><path d="M2.5 1.5v9l7-4.5z" fill="currentColor"/></svg>
<span id="vtRunLabel">npm start</span>
</button>
<span class="vt-run-hint" id="vtRunHint">runs the dev server</span>
</div>
</div>
</div>
</div>
</div>
<div class="vt-window-resize" id="vtWindowHandle" title="Drag to resize window"><span></span></div>
</div>
<div class="vt-output-label">Output</div>
<div id="vtOutputArea">
<p class="vt-output-placeholder">Run <code style="font-family:var(--mono-font)">npm start</code> above to see it here.</p>
<div class="vt-browser" id="vtBrowser">
<div class="vt-browser-bar">
<div class="vt-dots"><span></span><span></span><span></span></div>
<div class="vt-browser-url">localhost:3000</div>
</div>
<header class="vt-header theme-light" id="vtHeader" style="display:none;">
<p id="vtHeaderMsg">Hello, Alex. You have 0 items in your cart.</p>
<div class="vt-header-btns">
<button class="vt-mini-btn btn-cart" id="vtCartBtn" type="button">Add to cart</button>
<button class="vt-mini-btn btn-theme" id="vtThemeBtn" type="button">Toggle theme</button>
<button class="vt-mini-btn btn-logout" id="vtLogoutBtn" type="button">Logout</button>
</div>
</header>
<div class="vt-login" id="vtLogin">
<p class="vt-login-msg">Please log in</p>
<button class="vt-mini-btn btn-cart" id="vtLoginBtn" type="button">Log in as Alex</button>
</div>
</div>
</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('vt');
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);
});
/* --- file switching --- */
var files = {
composer:
'<span class="tok-comment">// Flattens N nested providers into one component</span>\n' +
'<span class="tok-kw">const</span> <span class="tok-type">ContextComposer</span> <span class="tok-punc">=</span> <span class="tok-punc">({</span> providers<span class="tok-punc">,</span> children <span class="tok-punc">}) => {</span>\n' +
' <span class="tok-kw">return</span> <span class="tok-punc">(</span>\n' +
' <span class="tok-punc"><></span>\n' +
' <span class="tok-punc">{</span>providers.reduceRight<span class="tok-punc">((</span>acc<span class="tok-punc">,</span> <span class="tok-type">Provider</span><span class="tok-punc">) => {</span>\n' +
' <span class="tok-kw">return</span> <span class="tok-punc"><</span><span class="tok-type">Provider</span><span class="tok-punc">>{</span>acc<span class="tok-punc">}</</span><span class="tok-type">Provider</span><span class="tok-punc">>;</span>\n' +
' <span class="tok-punc">},</span> children<span class="tok-punc">)}</span>\n' +
' <span class="tok-punc"></></span>\n' +
' <span class="tok-punc">);</span>\n' +
'<span class="tok-punc">};</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">default</span> <span class="tok-type">ContextComposer</span><span class="tok-punc">;</span>',
authcontext:
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> createContext<span class="tok-punc">,</span> useContext<span class="tok-punc">,</span> useState<span class="tok-punc">,</span> useMemo <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'react\'</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-kw">const</span> <span class="tok-type">AuthContext</span> <span class="tok-punc">=</span> createContext<span class="tok-punc">();</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">function</span> <span class="tok-type">AuthProvider</span><span class="tok-punc">({</span> children <span class="tok-punc">}) {</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">[</span>user<span class="tok-punc">,</span> setUser<span class="tok-punc">]</span> <span class="tok-punc">=</span> useState<span class="tok-punc">(</span><span class="tok-kw">null</span><span class="tok-punc">);</span>\n\n' +
' <span class="tok-kw">const</span> login <span class="tok-punc">=</span> <span class="tok-punc">(</span>userData<span class="tok-punc">) =></span> setUser<span class="tok-punc">(</span>userData<span class="tok-punc">);</span>\n' +
' <span class="tok-kw">const</span> logout <span class="tok-punc">=</span> <span class="tok-punc">() =></span> setUser<span class="tok-punc">(</span><span class="tok-kw">null</span><span class="tok-punc">);</span>\n\n' +
' <span class="tok-kw">const</span> value <span class="tok-punc">=</span> useMemo<span class="tok-punc">(() => ({</span> user<span class="tok-punc">,</span> login<span class="tok-punc">,</span> logout <span class="tok-punc">}),</span> <span class="tok-punc">[</span>user<span class="tok-punc">]);</span>\n\n' +
' <span class="tok-kw">return</span> <span class="tok-punc"><</span><span class="tok-type">AuthContext</span><span class="tok-punc">.</span><span class="tok-tag">Provider</span> <span class="tok-attr">value</span><span class="tok-punc">={</span>value<span class="tok-punc">}>{</span>children<span class="tok-punc">}</</span><span class="tok-type">AuthContext</span><span class="tok-punc">.</span><span class="tok-tag">Provider</span><span class="tok-punc">>;</span>\n' +
'<span class="tok-punc">}</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">const</span> useAuth <span class="tok-punc">=</span> <span class="tok-punc">() =></span> useContext<span class="tok-punc">(</span><span class="tok-type">AuthContext</span><span class="tok-punc">);</span>',
themecontext:
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> createContext<span class="tok-punc">,</span> useContext<span class="tok-punc">,</span> useState<span class="tok-punc">,</span> useMemo <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'react\'</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-kw">const</span> <span class="tok-type">ThemeContext</span> <span class="tok-punc">=</span> createContext<span class="tok-punc">();</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">function</span> <span class="tok-type">ThemeProvider</span><span class="tok-punc">({</span> children <span class="tok-punc">}) {</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">[</span>theme<span class="tok-punc">,</span> setTheme<span class="tok-punc">]</span> <span class="tok-punc">=</span> useState<span class="tok-punc">(</span><span class="tok-str">\'light\'</span><span class="tok-punc">);</span>\n' +
' <span class="tok-kw">const</span> toggleTheme <span class="tok-punc">=</span> <span class="tok-punc">() =></span> setTheme<span class="tok-punc">(</span>t <span class="tok-punc">=></span> t <span class="tok-punc">===</span> <span class="tok-str">\'light\'</span> <span class="tok-punc">?</span> <span class="tok-str">\'dark\'</span> <span class="tok-punc">:</span> <span class="tok-str">\'light\'</span><span class="tok-punc">);</span>\n\n' +
' <span class="tok-comment">// Volatile data — re-memoized only when theme itself changes</span>\n' +
' <span class="tok-kw">const</span> value <span class="tok-punc">=</span> useMemo<span class="tok-punc">(() => ({</span> theme<span class="tok-punc">,</span> toggleTheme <span class="tok-punc">}),</span> <span class="tok-punc">[</span>theme<span class="tok-punc">]);</span>\n\n' +
' <span class="tok-kw">return</span> <span class="tok-punc"><</span><span class="tok-type">ThemeContext</span><span class="tok-punc">.</span><span class="tok-tag">Provider</span> <span class="tok-attr">value</span><span class="tok-punc">={</span>value<span class="tok-punc">}>{</span>children<span class="tok-punc">}</</span><span class="tok-type">ThemeContext</span><span class="tok-punc">.</span><span class="tok-tag">Provider</span><span class="tok-punc">>;</span>\n' +
'<span class="tok-punc">}</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">const</span> useTheme <span class="tok-punc">=</span> <span class="tok-punc">() =></span> useContext<span class="tok-punc">(</span><span class="tok-type">ThemeContext</span><span class="tok-punc">);</span>',
cartcontext:
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> createContext<span class="tok-punc">,</span> useContext<span class="tok-punc">,</span> useState<span class="tok-punc">,</span> useMemo <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'react\'</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-kw">const</span> <span class="tok-type">CartContext</span> <span class="tok-punc">=</span> createContext<span class="tok-punc">();</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">function</span> <span class="tok-type">CartProvider</span><span class="tok-punc">({</span> children <span class="tok-punc">}) {</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">[</span>itemCount<span class="tok-punc">,</span> setItemCount<span class="tok-punc">]</span> <span class="tok-punc">=</span> useState<span class="tok-punc">(</span><span class="tok-expr">0</span><span class="tok-punc">);</span>\n' +
' <span class="tok-kw">const</span> addItem <span class="tok-punc">=</span> <span class="tok-punc">() =></span> setItemCount<span class="tok-punc">(</span>n <span class="tok-punc">=></span> n <span class="tok-punc">+</span> <span class="tok-expr">1</span><span class="tok-punc">);</span>\n\n' +
' <span class="tok-comment">// Most volatile — re-memoized on every itemCount change</span>\n' +
' <span class="tok-kw">const</span> value <span class="tok-punc">=</span> useMemo<span class="tok-punc">(() => ({</span> itemCount<span class="tok-punc">,</span> addItem <span class="tok-punc">}),</span> <span class="tok-punc">[</span>itemCount<span class="tok-punc">]);</span>\n\n' +
' <span class="tok-kw">return</span> <span class="tok-punc"><</span><span class="tok-type">CartContext</span><span class="tok-punc">.</span><span class="tok-tag">Provider</span> <span class="tok-attr">value</span><span class="tok-punc">={</span>value<span class="tok-punc">}>{</span>children<span class="tok-punc">}</</span><span class="tok-type">CartContext</span><span class="tok-punc">.</span><span class="tok-tag">Provider</span><span class="tok-punc">>;</span>\n' +
'<span class="tok-punc">}</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">const</span> useCart <span class="tok-punc">=</span> <span class="tok-punc">() =></span> useContext<span class="tok-punc">(</span><span class="tok-type">CartContext</span><span class="tok-punc">);</span>',
app:
'<span class="tok-kw">import</span> <span class="tok-type">ContextComposer</span> <span class="tok-kw">from</span> <span class="tok-str">\'./ContextComposer\'</span><span class="tok-punc">;</span>\n' +
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> <span class="tok-type">AuthProvider</span> <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'./AuthContext\'</span><span class="tok-punc">;</span>\n' +
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> <span class="tok-type">ThemeProvider</span> <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'./ThemeContext\'</span><span class="tok-punc">;</span>\n' +
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> <span class="tok-type">CartProvider</span> <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'./CartContext\'</span><span class="tok-punc">;</span>\n' +
'<span class="tok-kw">import</span> <span class="tok-type">AppContent</span> <span class="tok-kw">from</span> <span class="tok-str">\'./AppContent\'</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-kw">function</span> <span class="tok-type">App</span><span class="tok-punc">() {</span>\n' +
' <span class="tok-kw">return</span> <span class="tok-punc">(</span>\n' +
' <span class="tok-punc"><</span><span class="tok-type">ContextComposer</span> <span class="tok-attr">providers</span><span class="tok-punc">={[</span><span class="tok-type">AuthProvider</span><span class="tok-punc">,</span> <span class="tok-type">ThemeProvider</span><span class="tok-punc">,</span> <span class="tok-type">CartProvider</span><span class="tok-punc">]}></span>\n' +
' <span class="tok-punc"><</span><span class="tok-type">AppContent</span> <span class="tok-punc">/></span>\n' +
' <span class="tok-punc"></</span><span class="tok-type">ContextComposer</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc">);</span>\n' +
'<span class="tok-punc">}</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">default</span> <span class="tok-type">App</span><span class="tok-punc">;</span>',
appcontent:
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> useAuth <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'./AuthContext\'</span><span class="tok-punc">;</span>\n' +
'<span class="tok-kw">import</span> <span class="tok-type">LoginPage</span> <span class="tok-kw">from</span> <span class="tok-str">\'./LoginPage\'</span><span class="tok-punc">;</span>\n' +
'<span class="tok-kw">import</span> <span class="tok-type">Header</span> <span class="tok-kw">from</span> <span class="tok-str">\'./Header\'</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-comment">// Gate the whole app on auth state — everything below</span>\n' +
'<span class="tok-comment">// this point can safely assume `user` exists.</span>\n' +
'<span class="tok-kw">function</span> <span class="tok-type">AppContent</span><span class="tok-punc">() {</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">{</span> user <span class="tok-punc">}</span> <span class="tok-punc">=</span> useAuth<span class="tok-punc">();</span>\n\n' +
' <span class="tok-kw">return</span> user <span class="tok-punc">?</span> <span class="tok-punc"><</span><span class="tok-type">Header</span> <span class="tok-punc">/></span> <span class="tok-punc">:</span> <span class="tok-punc"><</span><span class="tok-type">LoginPage</span> <span class="tok-punc">/>;</span>\n' +
'<span class="tok-punc">}</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">default</span> <span class="tok-type">AppContent</span><span class="tok-punc">;</span>',
loginpage:
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> useAuth <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'./AuthContext\'</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-kw">function</span> <span class="tok-type">LoginPage</span><span class="tok-punc">() {</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">{</span> login <span class="tok-punc">}</span> <span class="tok-punc">=</span> useAuth<span class="tok-punc">();</span>\n\n' +
' <span class="tok-kw">return</span> <span class="tok-punc">(</span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">div</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">p</span><span class="tok-punc">></span>Please log in<span class="tok-punc"></</span><span class="tok-tag">p</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">button</span> <span class="tok-attr">onClick</span><span class="tok-punc">={</span><span class="tok-punc">()</span> <span class="tok-punc">=></span> login<span class="tok-punc">({</span> <span class="tok-attr">name</span><span class="tok-punc">:</span> <span class="tok-str">\'Alex\'</span> <span class="tok-punc">})}></span>\n' +
' Log in as Alex\n' +
' <span class="tok-punc"></</span><span class="tok-tag">button</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"></</span><span class="tok-tag">div</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc">);</span>\n' +
'<span class="tok-punc">}</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">default</span> <span class="tok-type">LoginPage</span><span class="tok-punc">;</span>',
header:
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> useAuth <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'./AuthContext\'</span><span class="tok-punc">;</span>\n' +
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> useTheme <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'./ThemeContext\'</span><span class="tok-punc">;</span>\n' +
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> useCart <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">\'./CartContext\'</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-kw">function</span> <span class="tok-type">Header</span><span class="tok-punc">() {</span>\n' +
' <span class="tok-comment">// You can call as many hooks as you need</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">{</span> user<span class="tok-punc">,</span> logout <span class="tok-punc">}</span> <span class="tok-punc">=</span> useAuth<span class="tok-punc">();</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">{</span> theme<span class="tok-punc">,</span> toggleTheme <span class="tok-punc">}</span> <span class="tok-punc">=</span> useTheme<span class="tok-punc">();</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">{</span> itemCount<span class="tok-punc">,</span> addItem <span class="tok-punc">}</span> <span class="tok-punc">=</span> useCart<span class="tok-punc">();</span>\n\n' +
' <span class="tok-kw">return</span> <span class="tok-punc">(</span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">header</span> <span class="tok-attr">className</span><span class="tok-punc">={</span>theme<span class="tok-punc">}></span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">p</span><span class="tok-punc">></span>Hello<span class="tok-punc">,</span> <span class="tok-punc">{</span>user.name<span class="tok-punc">}.</span> You have <span class="tok-punc">{</span>itemCount<span class="tok-punc">}</span> items in your cart.<span class="tok-punc"></</span><span class="tok-tag">p</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">button</span> <span class="tok-attr">onClick</span><span class="tok-punc">={</span>logout<span class="tok-punc">}></span>Logout<span class="tok-punc"></</span><span class="tok-tag">button</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"></</span><span class="tok-tag">header</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc">);</span>\n' +
'<span class="tok-punc">}</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">default</span> <span class="tok-type">Header</span><span class="tok-punc">;</span>'
};
var codeEl = root.querySelector('#vtCode');
var rows = root.querySelectorAll('.vt-row');
var tabs = root.querySelectorAll('.vt-tab');
function selectFile(id) {
codeEl.innerHTML = files[id];
rows.forEach(function (r) { r.classList.toggle('is-selected', r.getAttribute('data-file') === id); });
tabs.forEach(function (t) { t.classList.toggle('is-active', t.getAttribute('data-file') === id); });
}
rows.forEach(function (r) { r.addEventListener('click', function () { selectFile(r.getAttribute('data-file')); }); });
tabs.forEach(function (t) { t.addEventListener('click', function () { selectFile(t.getAttribute('data-file')); }); });
selectFile('app');
/* --- terminal run simulation --- */
var termLines = root.querySelector('#vtTermLines');
var runBtn = root.querySelector('#vtRunBtn');
var runLabel = root.querySelector('#vtRunLabel');
var runHint = root.querySelector('#vtRunHint');
var browser = root.querySelector('#vtBrowser');
var placeholder = root.querySelector('.vt-output-placeholder');
var running = false;
var lines = [
{ text: '$ npm start', cls: 'is-cmd', delay: 0 },
{ text: 'Starting the development server\u2026', cls: '', delay: 500 },
{ text: 'Compiled successfully!', cls: 'is-ok', delay: 1350 },
{ text: 'You can now view multi-context-demo in the browser.', cls: '', delay: 1600 },
{ text: ' Local: http://localhost:3000', cls: '', delay: 1850 }
];
runBtn.addEventListener('click', function () {
if (running) return;
running = true;
runBtn.disabled = true;
runLabel.textContent = 'running\u2026';
browser.classList.remove('is-shown');
placeholder.style.display = 'block';
termLines.innerHTML = '';
lines.forEach(function (line) {
setTimeout(function () {
var div = document.createElement('div');
div.className = 'vt-term-line ' + line.cls;
div.textContent = line.text;
termLines.appendChild(div);
}, line.delay);
});
setTimeout(function () {
placeholder.style.display = 'none';
browser.classList.add('is-shown');
runBtn.disabled = false;
runLabel.textContent = 'npm start';
runHint.textContent = 'server running \u2014 rerun anytime';
running = false;
}, 2300);
});
/* --- resize handles (pointer-capture drag) --- */
function makeResizable(handle, opts) {
var dragging = false, startPos = 0, startSize = 0;
handle.addEventListener('pointerdown', function (e) {
dragging = true;
handle.classList.add('is-dragging');
startPos = opts.axis === 'x' ? e.clientX : e.clientY;
startSize = opts.target.getBoundingClientRect()[opts.axis === 'x' ? 'width' : 'height'];
handle.setPointerCapture(e.pointerId);
e.preventDefault();
});
handle.addEventListener('pointermove', function (e) {
if (!dragging) return;
var pos = opts.axis === 'x' ? e.clientX : e.clientY;
var delta = pos - startPos;
if (opts.invert) delta = -delta;
var size = Math.min(opts.max, Math.max(opts.min, startSize + delta));
opts.target.style[opts.prop] = size + 'px';
if (opts.onResize) opts.onResize(size);
});
function stop() { dragging = false; handle.classList.remove('is-dragging'); }
handle.addEventListener('pointerup', stop);
handle.addEventListener('pointercancel', stop);
}
var sidebar = root.querySelector('.vt-sidebar');
var sidebarHandle = root.querySelector('#vtSidebarHandle');
var lastSidebarWidth = sidebar.getBoundingClientRect().width || 190;
makeResizable(sidebarHandle, {
axis: 'x', target: sidebar, prop: 'width', min: 120, max: 320, invert: false,
onResize: function (s) { lastSidebarWidth = s; }
});
var terminal = root.querySelector('.vt-terminal');
var terminalHandle = root.querySelector('#vtTerminalHandle');
var lastTerminalHeight = terminal.getBoundingClientRect().height || 150;
makeResizable(terminalHandle, {
axis: 'y', target: terminal, prop: 'height', min: 60, max: 280, invert: true,
onResize: function (s) { lastTerminalHeight = s; }
});
var vtBody = root.querySelector('.vt-body');
var windowHandle = root.querySelector('#vtWindowHandle');
makeResizable(windowHandle, {
axis: 'y', target: vtBody, prop: 'height', min: 320, max: 620, invert: false
});
/* --- show/hide toggles --- */
var toggleSidebarBtn = root.querySelector('#vtToggleSidebar');
var sidebarHidden = false;
toggleSidebarBtn.addEventListener('click', function () {
sidebarHidden = !sidebarHidden;
if (sidebarHidden) {
var w = sidebar.getBoundingClientRect().width;
if (w > 0) lastSidebarWidth = w;
sidebar.classList.add('is-hidden');
sidebarHandle.style.display = 'none';
} else {
sidebar.classList.remove('is-hidden');
sidebar.style.width = lastSidebarWidth + 'px';
sidebarHandle.style.display = '';
}
});
var toggleTerminalBtn = root.querySelector('#vtToggleTerminal');
var terminalCollapsed = false;
toggleTerminalBtn.addEventListener('click', function () {
terminalCollapsed = !terminalCollapsed;
if (terminalCollapsed) {
var h = terminal.getBoundingClientRect().height;
if (h > 30) lastTerminalHeight = h;
terminal.classList.add('is-collapsed');
terminalHandle.style.display = 'none';
} else {
terminal.classList.remove('is-collapsed');
terminal.style.height = lastTerminalHeight + 'px';
terminalHandle.style.display = '';
}
});
/* --- real behavior: AppContent gating LoginPage vs Header on auth state --- */
var headerEl = root.querySelector('#vtHeader');
var loginEl = root.querySelector('#vtLogin');
var msgEl = root.querySelector('#vtHeaderMsg');
var renderEl = root.querySelector('#vtRenderCount');
var cartBtn = root.querySelector('#vtCartBtn');
var themeBtn = root.querySelector('#vtThemeBtn');
var logoutBtn = root.querySelector('#vtLogoutBtn');
var loginBtn = root.querySelector('#vtLoginBtn');
var user = null;
var itemCount = 0;
var theme = 'light';
var renders = 1;
function renderApp() {
if (user) {
loginEl.style.display = 'none';
headerEl.style.display = '';
renders += 1;
headerEl.className = 'vt-header theme-' + theme;
msgEl.textContent = 'Hello, ' + user.name + '. You have ' + itemCount + ' items in your cart.';
renderEl.textContent = 'Header renders: ' + renders;
} else {
headerEl.style.display = 'none';
loginEl.style.display = '';
}
}
renderApp();
loginBtn.addEventListener('click', function () {
user = { name: 'Alex' }; // AuthContext's login(userData)
itemCount = 0;
theme = 'light';
renders = 1;
renderApp(); // AppContent swaps LoginPage out for Header
});
logoutBtn.addEventListener('click', function () {
user = null; // AuthContext's logout()
renderApp(); // AppContent swaps Header out for LoginPage
});
cartBtn.addEventListener('click', function () {
itemCount += 1; // CartContext's addItem()
renderApp(); // Header re-renders because useCart()'s value changed
});
themeBtn.addEventListener('click', function () {
theme = theme === 'light' ? 'dark' : 'light'; // ThemeContext's toggleTheme()
renderApp(); // Header re-renders because useTheme()'s value changed
});
})();
</script>💊 Summary Table
Feature | Single Mega-Context | Multiple Focused Contexts |
|---|---|---|
Render Speed | Slower (everything updates) | Faster (only relevant parts update) |
Code Organization | Messy / Bloated | Organized / Modular |
Logic Scoping | Hard to track state logic | Logic is isolated to specific domains |