let query = '';
let computeCount = 0;
function useMemo(factory, deps) {
let cached = null;
return function get(newDeps) {
const same = cached && newDeps.every((d,i) => d === cached.deps[i]);
if (same) return cached.value;
computeCount++;
cached = { value: factory(), deps: newDeps };
return cached.value;
};
}
const items = ['apple', 'banana', 'apricot', 'blueberry'];
// Bug: items in deps but not query — query changes are ignored
const getFiltered = useMemo(
() => items.filter(x => x.startsWith(query)), // closes over query
[]
);
const r1 = getFiltered([items]); // query = ''
query = 'ap';
const r2 = getFiltered([items]); // query changed but items didn't — cache hit!
console.log(r1.length);
console.log(r2.length); // should be 2 (apple, apricot), returns 4 (stale)
console.log(computeCount);let query = '';
let computeCount = 0;
function useMemo(factory, deps) {
let cached = null;
return function get(newDeps) {
const same = cached && newDeps.every((d,i) => d === cached.deps[i]);
if (same) return cached.value;
computeCount++;
cached = { value: factory(), deps: newDeps };
return cached.value;
};
}
const items = ['apple', 'banana', 'apricot', 'blueberry'];
// Fix: include query in deps
const getFiltered = useMemo(
() => items.filter(x => x.startsWith(query)),
[]
);
const r1 = getFiltered([items, query]); // deps: [items, '']
query = 'ap';
const r2 = getFiltered([items, query]); // deps: [items, 'ap'] — changed!
console.log(r1.length);
console.log(r2.length);
console.log(computeCount);Bug: query is used in the factory but not in deps. When query changes, items stays the same — cache hits and returns the stale result computed with query=''.
Explanation: Including query in deps means the cache misses when query changes. r2 recomputes with the new query 'ap' → ['apple', 'apricot'] = 2 items.
Key Insight: Every variable used inside useMemo's factory must be in the deps array. The exhaustive-deps ESLint rule catches this. Missing deps = stale cached values.