react-hooks/preserve-manual-memoization
Disallow useMemo and useCallback calls that React Compiler cannot keep memoized
Also called react_hooks/preserve-manual-memoization, react/preserve-manual-memoization
React Compiler replaces your useMemo and useCallback calls with its own caching. If it can't cache a value that you memoized by hand, it skips the whole component rather than make it slower, and nothing in that component is optimized.
The usual cause is a dependency that the compiler can't cache, such as an object that's created before a hook call and changed after it. That object is new on every render, so the useMemo that depends on it never reuses its result.
Configure#
Off by default. To turn it on:
bunfig.toml
[lint.rules]
"react-hooks/preserve-manual-memoization" = "error" # or "warn"Examples#
example.jsx1 problem
import { useMemo } from "react";
function Table({ rows }) {
const columns = ["name"];
const isWide = useIsWide();
if (isWide) {
columns.push("email");
}
const header = useMemo(() => columns.map(name => name.toUpperCase()), [columns])Compilation Skipped: Existing memoization could not be preservedReact Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.;
return <Grid header={header} rows={rows} />;
}example.jsxNo problems
import { useMemo } from "react";
function Table({ rows }) {
const isWide = useIsWide();
const columns = isWide ? ["name", "email"] : ["name"];
const header = useMemo(() => columns.map(name => name.toUpperCase()), [columns]);
return <Grid header={header} rows={rows} />;
}When not to use it#
This rule only matters if you build with React Compiler.