Data Caching and Framework Loaders: Choose an Application Data Layer
Data Caching and Framework Loaders: Choose an Application Data Layer
A teacher revisits a student’s report and the page downloads the same data again. Separate panels can also request the same report independently, wasting work and showing inconsistent loading states.
A data layer coordinates fetching, identity, freshness and reuse across an application. A cache stores results under meaningful keys; a route loader or framework data API can arrange data before a route’s screen renders. React alone does not provide a complete universal network cache.
Understand
Choose a meaningful request key.
Build
Explain freshness, invalidation and shared requests.
Check
Recognize the boundaries of a local cache demonstration.
Request identity connects cached data to its consumer
Identify
Use a key that includes the report’s relevant inputs.
Look up
Reuse a fresh result or load a new one.
Refresh
Invalidate or update when the underlying report changes.
A cached value still needs a freshness rule
A student ID alone may be insufficient if reports vary by term, school or permissions. A key should include the inputs that identify the requested data. A cache hit means matching stored data exists; it does not prove that data is still valid or permitted for the current user.
Libraries and frameworks can coordinate repeated requests, background refresh, invalidation, errors and server integration. A route loader can start needed work before the page component runs, avoiding a parent-then-child Effect waterfall. These capabilities belong to the chosen tool and configuration, not to useEffect itself.
Inspect an explicitly local cache model
This local model has an immutable source table and a Map owned by this mounted App. The first lookup stores Maya’s report; another lookup reuses it. It performs no network request, has no server cache and is discarded when the App unmounts. Its visible messages teach hit versus miss.
React · JavaScript with JSX
import { useRef, useState } from 'react';
const reports = { 'student-1:term-1': 'Maya: 75' };
export default function App() {
const cache = useRef(new Map());
const [message, setMessage] = useState('No report requested');
function load() {
const key = 'student-1:term-1';
if (cache.current.has(key)) setMessage('Cache hit: ' + cache.current.get(key));
else { cache.current.set(key, reports[key]); setMessage('Local load: ' + reports[key]); }
}
return <main><h1>Local cache model</h1><p>{message}</p><button onClick={load}>Read Maya report</button></main>;
}Expected screen and behavior
First Read Maya report shows Local load: Maya: 75. The next click shows Cache hit: Maya: 75.Trace the local key and stored result
React · Inspect an explicitly local cache model
import { useRef, useState } from 'react';
const reports = { 'student-1:term-1': 'Maya: 75' };
export default function App() {
const cache = useRef(new Map());
const [message, setMessage] = useState('No report requested');
function load() {
const key = 'student-1:term-1';
if (cache.current.has(key)) setMessage('Cache hit: ' + cache.current.get(key));
else { cache.current.set(key, reports[key]); setMessage('Local load: ' + reports[key]); }
}
return <main><h1>Local cache model</h1><p>{message}</p><button onClick={load}>Read Maya report</button></main>;
}Initially the Map is empty. The event builds one explicit key containing student and term, checks has, and stores the source-table value on a miss. The next event finds the same key and retrieves the stored string. A ref retains the Map without using it as rendered state.
The displayed message is state, so React describes the result after each event. This arrangement is a teaching model for explicit cache operations, not a recommended network query library. It lacks subscriptions, concurrent-request deduplication, expiration and user-session handling.
Choose a framework or library by the application need
A framework loader may integrate with server rendering and navigation; a client query library may provide shared query state, invalidation and background refresh. Check the supported runtime, request key design and mutation workflow rather than choosing solely by a short code example.
A saved mark changes the report’s source of truth. Decide whether to update the cached report immediately, invalidate it for a fresh load, or both under the tool’s documented rules. Clear or segregate user-specific data when authentication changes so a previous user’s report is not reused inappropriately.
Try the school task
React · starting example
import { useRef, useState } from 'react';
const reports = { 'student-1:term-1': 'Maya: 75' };
export default function App() {
const cache = useRef(new Map());
const [message, setMessage] = useState('No report requested');
function load() {
const key = 'student-1:term-1';
if (cache.current.has(key)) setMessage('Cache hit: ' + cache.current.get(key));
else { cache.current.set(key, reports[key]); setMessage('Local load: ' + reports[key]); }
}
return <main><h1>Local cache model</h1><p>{message}</p><button onClick={load}>Read Maya report</button></main>;
}Add an explicit forget action to the local model. After a cache hit, forgetting the report must make the next lookup a local load again.
Worked answer
delete removes that key, and the message makes invalidation visible. The next read no longer finds the key. This demonstrates invalidation mechanically without claiming a production refresh strategy.
React · JavaScript with JSX
import { useRef, useState } from 'react';
export default function App() {
const cache = useRef(new Map());
const [message, setMessage] = useState('No report requested');
function read() {
const key = 'student-1:term-1';
if (cache.current.has(key)) setMessage('Cache hit: Maya: 75');
else { cache.current.set(key, 'Maya: 75'); setMessage('Local load: Maya: 75'); }
}
function forget() { cache.current.delete('student-1:term-1'); setMessage('Report forgotten'); }
return <main><h1>Local invalidation model</h1><p>{message}</p>
<button onClick={read}>Read report</button><button onClick={forget}>Forget report</button></main>;
}Expected screen and behavior
Read twice to see a cache hit. Forget report removes it; another read becomes a local load.Mistakes and boundaries
Do not reuse a module-level cache of private reports across unrelated server requests without an isolation policy. Browser component lifetime and server process lifetime are different.
Freshness, persistence and authorization are independent requirements. localStorage persistence does not make data current or secure.
Suspense does not automatically turn an arbitrary fetch Effect into a coordinated data loader. Use a supported Suspense-enabled source or framework API if that is the selected design.
Check your reasoning
React · Inspect an explicitly local cache model
import { useRef, useState } from 'react';
const reports = { 'student-1:term-1': 'Maya: 75' };
export default function App() {
const cache = useRef(new Map());
const [message, setMessage] = useState('No report requested');
function load() {
const key = 'student-1:term-1';
if (cache.current.has(key)) setMessage('Cache hit: ' + cache.current.get(key));
else { cache.current.set(key, reports[key]); setMessage('Local load: ' + reports[key]); }
}
return <main><h1>Local cache model</h1><p>{message}</p><button onClick={load}>Read Maya report</button></main>;
}Choose the answer that follows the data and the interaction, then read the explanation for each alternative.
Carry the idea forward
Give data a clear identity, freshness and invalidation policy. Connect this with route data needs and the underlying request mechanism.
Read the primary reference: React: alternatives to fetching in Effects.
Reaching the recap and completing the quizzes are separate milestones.