You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React使用useState对象时,如何避免相同值setState引发的初始重渲染?

减少React组件无用重渲染的问题处理

我明确知道自己漏了些关键点,但没找到清晰答案。现在想减少组件重渲染次数,尤其要避免这种无用渲染场景:

  • 状态用对象类型,想通过单次setState同时设置loading和products属性;
  • 初始loading设为true,因为useEffect至少会执行一次;
  • useEffect里调用setState会触发一次无意义的即时重渲染,这和React对对象的浅比较机制有关。

实际代码

export const ProductsIndex = () => {
  console.log('[ProductsIndex] Rendering');

  const [state, setState] = useState({ loading: true, products: [], pages: 1 });

  useEffect(() => {
    console.log('[ProductsIndex] useEffect');

    setState(prev => ({ ...prev, loading: true }));
    axios.get('/api/products', params)
      .then(res => {
        setState(prev => ({ ...prev, loading: false, products: res.data }));
      });

    // fetch
  }, [params]);
};

我想到的解决方案

useEffect(() => {
  console.log('[ProductsIndex] useEffect');

  setState(prev => prev.loading ? prev : { ...prev, loading: true });
}, []);

更优处理方式

1. 去掉冗余的初始loading设置

初始状态里loading已经是true,第一次进入useEffect时完全没必要再调用setState设置loading——新创建的对象引用和原状态不同,React浅比较后会判定状态变化,触发无意义重渲染。直接发起请求即可:

useEffect(() => {
  console.log('[ProductsIndex] useEffect');

  axios.get('/api/products', params)
    .then(res => {
      setState(prev => ({ ...prev, loading: false, products: res.data }));
    });
}, [params]);

2. 拆分独立状态(推荐优化)

如果状态中各属性更新频率不同,把它们拆成独立的useState,能彻底避免对象浅比较带来的不必要重渲染,逻辑也更清晰:

export const ProductsIndex = () => {
  console.log('[ProductsIndex] Rendering');

  const [loading, setLoading] = useState(true);
  const [products, setProducts] = useState([]);
  const [pages, setPages] = useState(1);

  useEffect(() => {
    console.log('[ProductsIndex] useEffect');
    // 仅当loading不是true时才更新,避免无用渲染
    if (!loading) setLoading(true);
    
    axios.get('/api/products', params)
      .then(res => {
        setLoading(false);
        setProducts(res.data);
      });
  }, [params, loading]);
};

3. 优化你的原有方案

如果坚持保留对象状态,记得把params加入依赖数组(否则参数变化时不会重新请求),同时简化判断逻辑:

useEffect(() => {
  console.log('[ProductsIndex] useEffect');

  setState(prev => prev.loading ? prev : { ...prev, loading: true });

  axios.get('/api/products', params)
    .then(res => {
      setState(prev => ({ ...prev, loading: false, products: res.data }));
    });
}, [params]);

内容的提问来源于stack exchange,提问作者gremo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 21:30:30