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

React中Accordion展开时Map渲染表格加载缓慢问题及优化需求

解决方案:优化Accordion展开时的表格渲染性能

一、核心性能问题定位

你遇到的慢渲染问题根源在于setIndex触发父组件重渲染时,导致所有Accordion及内部表格都重复渲染,再加上key使用不当、函数引用不稳定等问题放大了开销。以下是具体优化步骤:


二、具体优化措施

1. 修复列表项的key值

不要用数组索引i作为TableRow的key,改用数据的唯一标识(比如item.resource_id)。索引作为key会导致React在列表更新时错误复用组件,增加不必要的DOM操作:

<TableRow key={item.resource_id}> {/* 替换原有的key={i} */}
  <TableCell>{item.resource_id}</TableCell>
  <TableCell>{item.resource_type}</TableCell>
  <TableCell align="center">{item.observerd_at}</TableCell>
  <TableCell align="center">{formatCurrency(item.saving)}</TableCell>
</TableRow>

2. 稳定handleChange函数引用

当前每次渲染都会创建新的handleChange函数,导致Accordion的onChange属性频繁变化,触发无意义重渲染。用useCallback包裹函数,保持引用稳定:

import { useCallback } from 'react';

// 替换原handleChange定义
const handleChange = useCallback((accountId) => (event, isExpanded) => {
  setIndex(isExpanded ? accountId : undefined);
}, [setIndex]);

3. 缩小重渲染范围

  • 用React.memo包裹AccountEvents组件,确保只有当data属性变化时才重渲染:
import { memo } from 'react';

const AccountEvents = memo(({ data }) => {
  // 原表格组件代码
});
  • 将单个Accordion拆成独立子组件,避免父组件状态变化时所有Accordion都重渲染:
const AccountAccordion = memo(({ account, recommendations, handleChange, currentIndex }) => {
  return (
    <Box sx={{ py: 2 }}>
      <Accordion 
        expanded={currentIndex === account.account_id}
        onChange={handleChange(account.account_id)}
      >
        <AccordionSummary
          expandIcon={<ExpandMoreIcon />}
          aria-controls={`panel-${account.account_id}-content`}
          id={`panel-${account.account_id}-header`}
        >
          <div className="row">
            <Typography align="left"><b>Account: </b>{account.account_name}</Typography>
            <Typography align="left"><b>Potential Monthly Savings: </b>{formatCurrency(account.total_saving)}</Typography>
          </div>
        </AccordionSummary>
        <AccordionDetails>
          <div className="row">
            <Divider color="grey" />
            <Box sx={{ py: 2 }}>
              <Typography align="left">
                <b>Account #: </b>{account.account_id}
              </Typography>
            </Box>
            <Divider color="grey" />
            <Box sx={{ py: 2 }}>
              <AccountEvents data={recommendations} />
            </Box>
          </div>
        </AccordionDetails>
      </Accordion>
    </Box>
  );
});

然后父组件中调用:

{props.accounts ?
  props.accounts.map((account) => (
    <AccountAccordion 
      key={account.account_id} 
      account={account} 
      recommendations={recommendations} 
      handleChange={handleChange}
      currentIndex={index}
    />
  ))
: <Typography>No accounts to display</Typography>}

4. 缓存格式化计算结果

如果formatCurrency有计算开销,用useMemo缓存每个项的格式化值,避免每次渲染重复计算:

import { useMemo } from 'react';

const AccountEvents = memo(({ data }) => {
  // 提前缓存所有格式化后的值
  const formattedData = useMemo(() => {
    return data?.map(item => ({
      ...item,
      formattedSaving: formatCurrency(item.saving)
    })) || [];
  }, [data]);

  return (
    <TableContainer component={Paper}>
      <Table size="small" aria-label="purchases" sx={{ p: 2 }}>
        {/* 表头代码不变 */}
        <TableBody>
          {formattedData.map((item) => (
            <TableRow key={item.resource_id}>
              {/* ...其他单元格 */}
              <TableCell align="center">{item.formattedSaving}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
});

三、加载状态处理

如果优化后仍有渲染延迟,可以通过DOM渲染完成的时机来控制加载状态:

import { memo, useState, useEffect, useRef } from 'react';
import CircularProgress from '@mui/material/CircularProgress';

const AccountEvents = memo(({ data }) => {
  const [isLoading, setIsLoading] = useState(true);
  const tableRef = useRef(null);

  useEffect(() => {
    // 表格DOM渲染完成后关闭加载状态
    if (tableRef.current) {
      setIsLoading(false);
    }
  }, [data]);

  if (!data) return <Typography>No data available</Typography>;

  return (
    <>
      {isLoading && (
        <div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
          <CircularProgress />
        </div>
      )}
      <TableContainer 
        component={Paper} 
        ref={tableRef} 
        style={{ display: isLoading ? 'none' : 'block' }}
      >
        {/* 原表格代码 */}
      </TableContainer>
    </>
  );
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:00:07