验证是否在副作用中同步调用 setState,这可能会导致重新渲染,从而降低性能。
¥Validates against calling setState synchronously in an effect, which can lead to re-renders that degrade performance.
规则详情
¥Rule Details
在副作用内部立即设置状态会强制 React 重新启动整个渲染周期。当你在副作用中更新状态时,React 必须重新渲染组件,将更改应用于 DOM,然后再次运行 effect。这会创建一个额外的渲染过程,而这可以通过在渲染期间直接转换数据或从属性获取状态来避免。改为在组件顶层转换数据。当属性或状态发生变化时,此代码将自然重新运行,而不会触发额外的渲染周期。
¥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 必须渲染两次:一次应用状态更新,然后再次运行 After 副作用。当单次渲染即可达到相同效果时,这种双重渲染非常浪费。
¥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.
在很多情况下,你可能根本不需要 effect。有关更多信息,请参阅 你可能不需要副作用。
¥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:
-
同步设置加载状态
¥Setting loading state synchronously
-
在副作用中从属性获取 state
¥Deriving state from props in effects
-
在效果中转换数据,而不是渲染
¥Transforming data in effects instead of render
无效
¥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>;
}
如果某些内容可以根据现有的属性或状态计算出来,请不要将其放入状态中。相反,在渲染过程中计算它。这使你的代码更快、更简单、更不容易出错。在 你可能不需要副作用 中了解更多信息。
¥When something can be calculated from the existing props or state, don’t put it in state. Instead, calculate it during rendering. This makes your code faster, simpler, and less error-prone. Learn more in You Might Not Need an Effect.