set-state-in-effect

验证是否在副作用中同步调用 setState,这可能会导致重新渲染,从而降低性能。

🌐 Validates against calling setState synchronously in an effect, which can lead to re-renders that degrade performance.

规则详情

🌐 Rule Details

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

🌐 Setting state immediately inside an effect forces React to restart the entire render cycle. When you update state in an effect, React must re-render your component, apply changes to the DOM, and then run effects again. This creates an extra render pass that could have been avoided by transforming data directly during render or deriving state from props. Transform data at the top level of your component instead. This code will naturally re-run when props or state change without triggering additional render cycles.

在副作用中同步的 setState 调用会在浏览器渲染前立即触发重新渲染,从而导致性能问题和视觉卡顿。React 必须渲染两次:第一次是为了应用状态更新,第二次是在副作用运行之后。对于可以通过一次渲染实现相同结果的情况,这种双重渲染是浪费资源的。

🌐 Synchronous setState calls in effects trigger immediate re-renders before the browser can paint, causing performance issues and visual jank. React has to render twice: once to apply the state update, then again after effects run. This double rendering is wasteful when the same result could be achieved with a single render.

在许多情况下,你可能根本不需要效果。更多信息请参阅 你可能不需要效果

🌐 In many cases, you may also not need an effect at all. Please see You Might Not Need an Effect for more information.

常见违规

🌐 Common Violations

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

🌐 This rule catches several patterns where synchronous setState is used unnecessarily:

  • 同步设置加载状态
  • 在副作用中从属性获取 state
  • 在效果中转换数据,而不是渲染

无效

🌐 Invalid

此规则的错误代码示例:

🌐 Examples of incorrect code for this rule:

// ❌ 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]);
}

有效

🌐 Valid

此规则的正确代码示例:

🌐 Examples of correct code for this rule:

// ✅ 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 中。 相反,应在渲染过程中计算它。这会让你的代码更快、更简单,并且减少错误。详情请参阅 You Might Not Need an Effect