知海

规则:set-state-in-effect

ReactAPI 参考:ESLint 插件

规则:set-state-in-effect

校验避免在 effect 中同步调用 setState,这可能导致重新渲染并降低性能。

规则详情

在 effect 中立即设置 state 会迫使 React 重新启动整个渲染周期。当你在 effect 中更新 state 时,React 必须重新渲染你的组件、将更改应用到 DOM,然后再次运行 effects。这产生了一次额外的渲染过程,而通过在渲染期间直接转换数据或从 props 派生 state 本可以避免。改为在组件顶层转换数据。这样代码会在 props 或 state 更改时自然重新运行,而不会触发额外的渲染周期。

effect 中的同步 setState 调用会在浏览器绘制之前触发立即重新渲染,导致性能问题和视觉卡顿。React 必须渲染两次:一次应用 state 更新,然后在 effects 运行后再次渲染。当相同的结果可以通过单次渲染实现时,这种双重渲染是浪费的。

在许多情况下,你可能根本不需要 effect。请参阅 你可能不需要 Effect 了解更多信息。

常见违规

此规则捕获了几种不必要使用同步 setState 的模式:

  • 同步设置 loading state
  • 在 effects 中从 props 派生 state
  • 在 effects 中转换数据,而不是在渲染期间

无效

此规则的不正确代码示例:

js 复制代码
// ❌ Synchronous setState in effect
function Component({data}) {
  const [items, setItems] = useState([]);

  useEffect(() => {
    setItems(data); // Extra render, use initial state instead
  }, [data]);
}

// ❌ Setting loading state synchronously
function Component() {
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true); // Synchronous, causes extra render
    fetchData().then(() => setLoading(false));
  }, []);
}

// ❌ Transforming data in effect
function Component({rawData}) {
  const [processed, setProcessed] = useState([]);

  useEffect(() => {
    setProcessed(rawData.map(transform)); // Should derive in render
  }, [rawData]);
}

// ❌ Deriving state from props
function Component({selectedId, items}) {
  const [selected, setSelected] = useState(null);

  useEffect(() => {
    setSelected(items.find(i => i.id === selectedId));
  }, [selectedId, items]);
}

有效

此规则的正确代码示例:

js 复制代码
// ✅ setState in an effect is fine if the value comes from a ref
function Tooltip() {
  const ref = useRef(null);
  const [tooltipHeight, setTooltipHeight] = useState(0);

  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTooltipHeight(height);
  }, []);
}

// ✅ Calculate during render
function Component({selectedId, items}) {
  const selected = items.find(i => i.id === selectedId);
  return <div>{selected?.name}</div>;
}

当某些东西可以从现有的 props 或 state 计算得出时,不要将其放入 state。 相反,在渲染期间计算它。这会使你的代码更快、更简单、更不容易出错。在 你可能不需要 Effect 中了解更多。

帮助我们改进文档

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