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

修改单个state后页面全量重载,大量图表重复加载如何解决?

问题描述
  • 开发包含大量图表的页面,使用Material UI菜单实现不同图表组的切换功能,点击MenuItem时修改part状态渲染对应组件数组
  • 核心问题:每次点击菜单按钮后所有图表都会触发一次快速加载,图表数量较多时严重影响页面使用体验
  • 调试情况:所有state均按需求正常赋值,useEffects也仅在页面初始化时执行一次,无多余的加载逻辑
  • 效果演示:charts load
现有代码

渲染代码

<>
  <div className={classes.actionBar}>
    <SelectEstimatedMonthForm />
  </div>
  <div>
    <Card>
      <div>
        <h1>Gráficos de DRE</h1>
        <Button
          aria-controls="simple-menu"
          aria-haspopup="true"
          onClick={handleClick}
        >
          Open Menu
        </Button>
        <Menu
          id="simple-menu"
          anchorEl={anchorEl}
          keepMounted
          open={Boolean(anchorEl)}
          onClose={handleClose}
        >
          <MenuItem onClick={handleClose}>Todos Gráficos</MenuItem>
          <MenuItem onClick={handleClose}>
            Gráficos DRE Orçamento Evolução
          </MenuItem>
          <MenuItem onClick={handleClose}>
            Gráficos de Evolução Parte 2
          </MenuItem>
          <MenuItem onClick={handleClose}>
            Gráficos de Representatividade
          </MenuItem>
        </Menu>
      </div>
      <div>
        {chartDataPart?.part1 && part === 1 && values && (
          <div>
            {chartDataPart?.part1?.map((data) => {
              if (data?.type === "bar") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartBar
                        data={data}
                        text={data?.text}
                        noLabel={data?.noLabel}
                        stacked={data?.stacked}
                        newPlugin={data?.customPlugin}
                      />
                    )}
                  </div>
                );
              } else if (data?.type === "pie") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartPie text={data?.text} data={data} />
                    )}
                  </div>
                );
              } else if (data?.type === "doughnut") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartDoughnut text={data?.text} data={data} />
                    )}
                  </div>
                );
              } else {
                return (
                  <div>
                    <h1>{data?.text} esta sem type</h1>
                  </div>
                );
              }
            })}
          </div>
        )}
        {chartDataPart?.part2 && part === 2 && values && (
          <div>
            {chartDataPart?.part2?.map((data) => {
              if (data?.type === "bar") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartBar
                        data={data}
                        text={data?.text}
                        noLabel={data?.noLabel}
                        stacked={data?.stacked}
                        newPlugin={data?.customPlugin}
                      />
                    )}
                  </div>
                );
              } else if (data?.type === "pie") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartPie text={data?.text} data={data} />
                    )}
                  </div>
                );
              } else if (data?.type === "doughnut") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartDoughnut text={data?.text} data={data} />
                    )}
                  </div>
                );
              } else {
                return (
                  <div>
                    <h1>{data?.text} esta sem type</h1>
                  </div>
                );
              }
            })}
          </div>
        )}
        {chartDataPart?.part3 && part === 3 && values && (
          <div>
            {chartDataPart?.part3?.map((data) => {
              if (data?.type === "bar") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartBar
                        data={data}
                        text={data?.text}
                        noLabel={data?.noLabel}
                        stacked={data?.stacked}
                        newPlugin={data?.customPlugin}
                      />
                    )}
                  </div>
                );
              } else if (data?.type === "pie") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartPie text={data?.text} data={data} />
                    )}
                  </div>
                );
              } else if (data?.type === "doughnut") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartDoughnut text={data?.text} data={data} />
                    )}
                  </div>
                );
              } else {
                return (
                  <div>
                    <h1>{data?.text} esta sem type</h1>
                  </div>
                );
              }
            })}
          </div>
        )}
        {chartData && part === 0 && values && (
          <div>
            {chartData?.map((data) => {
              if (data?.type === "bar") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartBar
                        data={data}
                        text={data?.text}
                        noLabel={data?.noLabel}
                        stacked={data?.stacked}
                        newPlugin={data?.customPlugin}
                      />
                    )}
                  </div>
                );
              } else if (data?.type === "pie") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartPie text={data?.text} data={data} />
                    )}
                  </div>
                );
              } else if (data?.type === "doughnut") {
                return (
                  <div>
                    {data?.labels && (
                      <ChartDoughnut text={data?.text} data={data} />
                    )}
                  </div>
                );
              } else {
                return (
                  <div>
                    <h1>{data?.text} esta sem type</h1>
                  </div>
                );
              }
            })}
          </div>
        )}
      </div>
    </Card>
 </div>
</>

副作用逻辑代码

useEffect(() => {
    const fetchDreData = async () => {
      try {
        const res = await http.get(
          `call1`
        );
        const data = await res.data;
        const final = data.data;
        setEstimatedMonths(final);
      } catch (e) {
        console.log("error", e);
      }
    };
    const fetchProductsData = async () => {
      try {
        const res = await http.get(
          `call2`
        );
        const data = await res.data;
        const final = data.data;
        setEstimatedProducts(final);
      } catch (e) {
        console.log("error", e);
      }
    };
    fetchDreData();
    fetchProductsData();
  }, [estimatedMonthContext.monthId]);

  useEffect(() => {
    if (estimatedProducts && estimatedMonths) {
      flatData();
    }
  }, [estimatedProducts, estimatedMonths]);

  useLayoutEffect(() => {
    chart();
  }, [values]);
优化方案

核心问题根因

当前你使用条件渲染控制不同图表组的显示隐藏,每次part变化时,旧图表组的DOM会被完全卸载,新图表组的DOM会重新挂载,所有图表都会重新走初始化逻辑,因此触发加载动画。

具体优化点

1. 渲染逻辑修改

将条件卸载改为CSS隐藏,保留DOM实例,切换时无需重新初始化图表:

// 替换原有条件渲染逻辑
<div style={{display: part === 1 ? 'block' : 'none'}}>
  {chartDataPart?.part1 && values && /* 原part1的图表渲染逻辑 */}
</div>
<div style={{display: part === 2 ? 'block' : 'none'}}>
  {chartDataPart?.part2 && values && /* 原part2的图表渲染逻辑 */}
</div>
<div style={{display: part === 3 ? 'block' : 'none'}}>
  {chartDataPart?.part3 && values && /* 原part3的图表渲染逻辑 */}
</div>
<div style={{display: part === 0 ? 'block' : 'none'}}>
  {chartData && values && /* 原part0的图表渲染逻辑 */}
</div>

2. 冗余代码抽离

4组图表的渲染逻辑完全一致,可以抽成通用组件减少重复代码:

// 抽通用图表组渲染组件
const ChartGroup = ({dataList}) => {
  return dataList?.map((data, idx) => {
    if (!data?.labels) return null
    switch(data.type) {
      case 'bar': 
        return <ChartBar key={idx} data={data} text={data.text} noLabel={data.noLabel} stacked={data.stacked} newPlugin={data.customPlugin} />
      case 'pie':
        return <ChartPie key={idx} text={data.text} data={data} />
      case 'doughnut':
        return <ChartDoughnut key={idx} text={data.text} data={data} />
      default:
        return <h1 key={idx}>{data?.text} esta sem type</h1>
    }
  })
}

// 调用时直接传入对应分组数据即可
<div style={{display: part === 1 ? 'block' : 'none'}}>
  {chartDataPart?.part1 && values && <ChartGroup dataList={chartDataPart.part1} />}
</div>

3. 性能兜底优化

  • 给所有map渲染的元素加唯一key,优先用数据内的唯一ID而非索引,减少不必要的重渲染
  • 给图表组件包裹React.memo,仅当props变化时才重新渲染
  • 图表数量极多时可增加虚拟滚动,仅渲染可视区域内的图表

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 10:39:03