incompatible-library

验证是否使用了与 memoization 不兼容的库(手动或自动)。

¥Validates against usage of libraries which are incompatible with memoization (manual or automatic).

注意

这些库是在 React 的 memoization 规则完整记录之前设计的。他们当时做出了正确的选择,以符合人机工程学的方式进行优化,使组件在应用状态变化时保持适当的响应度。虽然这些遗留模式有效,但我们发现它与 React 的编程模型不兼容。我们将继续与库作者合作,迁移这些库以使用遵循 React 规则的模式。

¥These libraries were designed before React’s memoization rules were fully documented. They made the correct choices at the time to optimize for ergonomic ways to keep components just the right amount of reactive as app state changes. While these legacy patterns worked, we have since discovered that it’s incompatible with React’s programming model. We will continue working with library authors to migrate these libraries to use patterns that follow the Rules of React.

规则详情

¥Rule Details

某些库使用了 React 不支持的模式。当 linter 从 已知列表 检测到这些 API 的使用情况时,它会根据此规则对其进行标记。这意味着 React Compiler 可以自动跳过使用这些不兼容 API 的组件,以避免破坏你的应用。

¥Some libraries use patterns that aren’t supported by React. When the linter detects usages of these APIs from a known list, it flags them under this rule. This means that React Compiler can automatically skip over components that use these incompatible APIs, in order to avoid breaking your app.

// Example of how memoization breaks with these libraries
function Form() {
const { watch } = useForm();

// ❌ This value will never update, even when 'name' field changes
const name = useMemo(() => watch('name'), [watch]);

return <div>Name: {name}</div>; // UI appears "frozen"
}

React Compiler 会根据 React 规则自动记忆值。如果手动 useMemo 出现问题,它也会破坏编译器的自动优化。此规则有助于识别这些有问题的模式。

¥React Compiler automatically memoizes values following the Rules of React. If something breaks with manual useMemo, it will also break the compiler’s automatic optimization. This rule helps identify these problematic patterns.

深入研究

设计遵循 React 规则的 API

¥Designing APIs that follow the Rules of React

在设计库 API 或钩子时需要考虑的一个问题是,调用 API 是否可以通过 useMemo 安全地进行记忆。如果无法激活,那么手动和 React 编译器的记忆功能都会破坏用户的代码。

¥One question to think about when designing a library API or hook is whether calling the API can be safely memoized with useMemo. If it can’t, then both manual and React Compiler memoizations will break your user’s code.

例如,“内部可变性” 就是这样一种不兼容的模式。内部可变性是指对象或函数保持其自身的隐藏状态,该状态会随时间变化,即使对它的引用保持不变。可以把它想象成一个外表相同但内容却秘密重新排列的盒子。React 无法识别任何更改,因为它只会检查你是否输入了不同的框,而不会检查框内的内容。这会破坏记忆机制,因为 React 依赖于外部对象(或函数)在其部分值发生变化时进行更改。

¥For example, one such incompatible pattern is “interior mutability”. Interior mutability is when an object or function keeps its own hidden state that changes over time, even though the reference to it stays the same. Think of it like a box that looks the same on the outside but secretly rearranges its contents. React can’t tell anything changed because it only checks if you gave it a different box, not what’s inside. This breaks memoization, since React relies on the outer object (or function) changing if part of its value has changed.

根据经验,在设计 React API 时,请考虑 useMemo 是否会破坏它:

¥As a rule of thumb, when designing React APIs, think about whether useMemo would break it:

function Component() {
const { someFunction } = useLibrary();
// it should always be safe to memoize functions like this
const result = useMemo(() => someFunction(), [someFunction]);
}

相反,设计返回不可变状态并使用显式更新函数的 API:

¥Instead, design APIs that return immutable state and use explicit update functions:

// ✅ Good: Return immutable state that changes reference when updated
function Component() {
const { field, updateField } = useLibrary();
// this is always safe to memo
const greeting = useMemo(() => `Hello, ${field.name}!`, [field.name]);

return (
<div>
<input
value={field.name}
onChange={(e) => updateField('name', e.target.value)}
/>
<p>{greeting}</p>
</div>
);
}

无效

¥Invalid

此规则的错误代码示例:

¥Examples of incorrect code for this rule:

// ❌ react-hook-form `watch`
function Component() {
const {watch} = useForm();
const value = watch('field'); // Interior mutability
return <div>{value}</div>;
}

// ❌ TanStack Table `useReactTable`
function Component({data}) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
// table instance uses interior mutability
return <Table table={table} />;
}

易犯错误

MobX

observer 这样的 MobX 模式也破坏了记忆假设,但 linter 尚未检测到它们。如果你依赖 MobX 并且发现你的应用无法与 React 编译器兼容,则可能需要使用 "use no memo" directive

¥MobX patterns like observer also break memoization assumptions, but the linter does not yet detect them. If you rely on MobX and find that your app doesn’t work with React Compiler, you may need to use the "use no memo" directive.

// ❌ MobX `observer`
const Component = observer(() => {
const [timer] = useState(() => new Timer());
return <span>Seconds passed: {timer.secondsPassed}</span>;
});

有效

¥Valid

此规则的正确代码示例:

¥Examples of correct code for this rule:

// ✅ For react-hook-form, use `useWatch`:
function Component() {
const {register, control} = useForm();
const watchedValue = useWatch({
control,
name: 'field'
});

return (
<>
<input {...register('field')} />
<div>Current value: {watchedValue}</div>
</>
);
}

其他一些库目前还没有与 React 的 memoization 模型兼容的替代 API。如果 linter 没有自动跳过调用这些 API 的组件或钩子,请使用 提交问题,以便我们将其添加到 linter 中。

¥Some other libraries do not yet have alternative APIs that are compatible with React’s memoization model. If the linter doesn’t automatically skip over your components or hooks that call these APIs, please file an issue so we can add it to the linter.