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

React表格组件持续重渲染:如何修复元素频繁刷新问题?

修复React表格持续重渲染及高亮失效问题

核心问题原因

你的表格行(<tr>)和单元格(<td>)持续重渲染、高亮无法保留的根本原因是在map循环中使用uuidv4()生成动态key。React的Diff算法依赖key识别DOM元素身份,每次组件渲染时uuidv4()都会生成新随机值,React会判定这些是全新元素,销毁旧DOM节点并重新创建,导致高亮状态丢失,同时造成不必要的性能损耗。

具体修复步骤

  1. 替换动态key为数据的唯一标识
    找到ratings数据中每个item的稳定唯一属性(比如item.name,如果棋类变体名称唯一;或者数据自带的id字段),将key设置为这个固定值,确保同一行的key在每次渲染时保持不变:

    <tr key={item.name}>
    
  2. 用useCallback缓存排序函数
    当前的vCycle、dCycle、rCycle函数会在组件每次渲染时重新创建,可能触发表头不必要重渲染。用useCallback包裹这些函数,保证函数引用稳定:

    import { React, useState, useEffect, useMemo, useCallback } from 'react';
    
    // ... 其他代码
    
    const vCycle = useCallback((currentVState) => {
      if (currentVState === 'default') {
        setVState('dsc');
        setDState('default');
        setRState('default');
      } else if (currentVState === 'dsc') {
        setVState('asc');
      } else {
        setVState('default');
      }
    }, []);
    
    const dCycle = useCallback((currentDState) => {
      if (currentDState === 'default') {
        setDState('dsc');
        setVState('default');
        setRState('default');
      } else if (currentDState === 'dsc') {
        setDState('asc');
      } else {
        setDState('default');
      }
    }, []);
    
    const rCycle = useCallback((currentRState) => {
      if (currentRState === 'default') {
        setRState('dsc');
        setVState('default');
        setDState('default');
      } else if (currentRState === 'dsc') {
        setRState('asc');
      } else {
        setRState('default');
      }
    }, []);
    
  3. 用useMemo缓存处理后的数据
    对Object.values(ratings).filter(...)的结果进行缓存,避免每次渲染重复执行过滤操作:

    const filteredRatings = useMemo(() => {
      return Object.values(ratings).filter(o => o.points.length);
    }, [ratings]);
    

    渲染时直接使用缓存后的数组:

    <tbody>
      {filteredRatings.map((item) => {
        return (
          <tr key={item.name}>
            <td className="variant">{item.name}</td>
            <td className='date'>{item.points.at(-1)[1] + 1 + "/" + item.points.at(-1)[2] + "/" + item.points.at(-1)[0].toString().slice(2, 4) }</td>
            <td className='rating'>{item.points.at(-1)[3]}</td>
          </tr>
        )
      }) ?? null}
    </tbody>
    

修改后的完整组件代码

import './Recent.css';
import { React, useState, useEffect, useMemo, useCallback } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSort, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons'

import * as fetch from 'node-fetch';

function Recent() {
  const [ratings, setRatings] = useState({});
  const [headers, setHeaders] = useState([""]);
  const [loaded, setLoaded] = useState(false);
  const [vState, setVState] = useState('default');
  const [dState, setDState] = useState('default');
  const [rState, setRState] = useState('default');

  const fetchRatings = () => {
    fetch('lichesslinkthatyoudontgettosee')
      .then (response => {
        return response.json()
      })
      .then (data => {
        setRatings(data);
        setHeaders(["Variant", "Date", "Rating"]);
        setLoaded(true);
      });
  }

  useEffect(() => {
    fetchRatings()
  }, []);

  const vCycle = useCallback((currentVState) => {
    if (currentVState === 'default') {
        setVState('dsc');
        setDState('default');
        setRState('default');
    } else if (currentVState === 'dsc') {
        setVState('asc');
    } else {
        setVState('default');
    }
  }, []);

  const dCycle = useCallback((currentDState) => {
    if (currentDState === 'default') {
        setDState('dsc');
        setVState('default');
        setRState('default');
    } else if (currentDState === 'dsc') {
        setDState('asc');
    } else {
        setDState('default');
    }
  }, []);

  const rCycle = useCallback((currentRState) => {
    if (currentRState === 'default') {
        setRState('dsc');
        setVState('default');
        setDState('default');
    } else if (currentRState === 'dsc') {
        setRState('asc');
    } else {
        setRState('default');
    }
  }, []);

  const filteredRatings = useMemo(() => {
    return Object.values(ratings).filter(o => o.points.length);
  }, [ratings]);

  return (
    <div className="container" style={{display: loaded ? 'flex' : 'none'}}>
      <h2>Most Recent</h2>
      <table>
        <thead>
          <tr>
            <th onClick={() => vCycle(vState)}>{headers[0]} <span><FontAwesomeIcon fixedWidth transform="shrink-3" icon={vState === 'default' ? faSort : vState === 'asc' ? faSortUp : faSortDown} /></span></th>
            <th onClick={() => dCycle(dState)}>{headers[1]} <span><FontAwesomeIcon fixedWidth transform="shrink-3" icon={dState === 'default' ? faSort : dState === 'asc' ? faSortUp : faSortDown} /></span></th>
            <th onClick={() => rCycle(rState)}>{headers[2]} <span><FontAwesomeIcon fixedWidth transform="shrink-3" icon={rState === 'default' ? faSort : rState === 'asc' ? faSortUp : faSortDown} /></span></th>
          </tr>
        </thead>
        <tbody>
           {filteredRatings.map((item) => {
                return (
                  <tr key={item.name}>
                    <td className="variant">{item.name}</td>
                    <td className='date'>{item.points.at(-1)[1] + 1 + "/" + item.points.at(-1)[2] + "/" + item.points.at(-1)[0].toString().slice(2, 4) }</td>
                    <td className='rating'>{item.points.at(-1)[3]}</td>
                  </tr>
                )
           }) ?? null}
        </tbody>
      </table>
    </div>
  );
}

export default Recent;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 14:12:03