react-hooks/memo-dependencies

Require the dependency list of useMemo and useCallback to match what the callback reads

has suggestions

Also called react_hooks/memo-dependencies, react/memo-dependencies

useMemo and useCallback keep their result until a value in the dependency list changes. If the callback reads a value that isn't in the list, the result stays the same when that value changes, and the component shows old data. A value in the list that the callback doesn't read makes it run again for nothing.

You don't have to list values that never change between renders, such as a setState function or a ref.

In .ts and .tsx files, the report comes with a suggestion that rewrites the list, which your editor can apply.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"react-hooks/memo-dependencies" = "error" # or "warn"

Examples#

Reported, because query is missing from the list:

example.jsx1 problem
import { useMemo } from "react";

function Results({ items, query }) {
  const visible = useMemo(() => items.filter(item => item.name.includes(query)), [items]);
  return <List items={visible} />;
}

Reported, because the callback doesn't read theme:

example.jsx1 problem
import { useCallback } from "react";

function SaveButton({ save, draft, theme }) {
  const onClick = useCallback(() => save(draft), [save, draft, theme]);
  return <button onClick={onClick}>Save</button>;
}
example.jsxNo problems
import { useCallback, useMemo, useState } from "react";

function Results({ items, query }) {
  const [selected, setSelected] = useState(null);
  const visible = useMemo(() => items.filter(item => item.name.includes(query)), [items, query]);
  const onSelect = useCallback(item => setSelected(item.id), []);
  return <List items={visible} selected={selected} onSelect={onSelect} />;
}

When not to use it#

react-hooks/exhaustive-deps checks the same two hooks, and effects too. With both rules on, the same list is reported twice.