知海

规则:incompatible-library

ReactAPI 参考:ESLint 插件

incompatible-library

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

规则详情 {/rule-details/}

某些库使用了 React 不支持的编程模式。当 linter 从已知列表中检测到这些 API 的使用时,会在此规则下标记它们。这意味着 React Compiler 可以自动跳过使用这些不兼容 API 的组件,以避免破坏你的应用。

js 复制代码
// 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 记忆化规则被完整记录之前设计的。它们在当时做出了正确的选择,以优化符合人体工学的方式,在应用状态变化时保持组件适度的响应性。虽然这些旧模式曾经可行,但我们后来发现它与 React 的编程模型不兼容。我们将继续与库作者合作,将这些库迁移为遵循 React 规则(Rules of React)的编程模式。

设计遵循 Rules of React 的 API {/designing-apis-that-follow-the-rules-of-react/}

在设计库 API 或 hook 时,需要考虑的一个问题是,调用该 API 是否可以安全地使用 useMemo 进行记忆化。如果不能,那么手动记忆化和 React Compiler 记忆化都会破坏用户的代码。

例如,其中一种不兼容模式是“内部可变性”(interior mutability)。内部可变性是指一个对象或函数即使引用保持不变,也会随时间保持自身隐藏状态并发生变化。可以把它想象成一个外面看起来一样,但内部会悄悄重排内容的盒子。React 无法判断任何东西发生了变化,因为它只检查你是否传入了一个不同的盒子,而不检查盒子里面是什么。这会破坏记忆化,因为 React 依赖外层对象(或函数)在其值的一部分发生变化时发生变化。

作为一个经验法则,在设计 React API 时,请思考 useMemo 是否会破坏它:

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

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

js 复制代码
// ✅ 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/}

本规则的错误代码示例:

js 复制代码
// ❌ 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 {/mobx/}

MobX 模式(如 observer)也会破坏记忆化假设,但 linter 目前尚未检测到它们。如果你依赖 MobX 并发现你的应用无法与 React Compiler 配合使用,你可能需要使用 "use no memo" 指令。

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

有效用法 {/valid/}

本规则的正确代码示例:

js 复制代码
// ✅ 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 记忆化模型兼容的替代 API。如果 linter 没有自动跳过这些调用这些 API 的组件或 hooks,请提交 issue,以便我们将其添加到 linter 中。

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。