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

为鼠标事件添加防抖函数时,如何避免子组件重复渲染?

问题分析与解决方案

问题根源

原代码中,相同order_id的子组件不会重复渲染,是因为当调用handleMouseEnter设置相同的currentOrderId时,React会检测到状态值未变化,跳过状态更新,父组件不重新渲染,子组件自然也不会重复渲染。

添加防抖后出现重复渲染的原因有两个:

  1. 直接在map中创建防抖函数(如onMouseEnter={() => debounce(...)}),每次父组件渲染时都会生成新的函数引用,子组件接收的props变化,触发重新渲染。
  2. 防抖函数未做状态判断,即使order_id相同,延迟触发的防抖逻辑仍会重复调用setCurOrderId,导致父组件多次渲染,子组件随之重复渲染。

解决方案

方案一:缓存不同order_id对应的防抖函数

用useRef存储每个order_id的防抖函数实例,确保同一order_id始终拿到相同的函数引用,同时配合useCallback稳定函数引用,避免子组件无意义渲染。

import { useState, useRef, useCallback } from 'react';
import debounce from 'lodash/debounce';

const ParentComponent = () => {
  const [currentOrderId, setCurOrderId] = useState(null);
  // 存储不同order_id的防抖函数
  const debounceEnterRef = useRef({});

  // 获取对应order_id的防抖函数,缓存复用
  const getDebouncedEnter = useCallback((orderId) => {
    if (!debounceEnterRef.current[orderId]) {
      debounceEnterRef.current[orderId] = debounce(() => {
        setCurOrderId(orderId);
      }, 300);
    }
    return debounceEnterRef.current[orderId];
  }, []);

  // 缓存离开事件的防抖函数
  const debouncedLeave = useCallback(debounce(() => {
    setCurOrderId(null);
  }, 300), []);

  return (
    <Box>
      {data.map(item => {
        const { order_id } = item;
        return (
          <GanttDiagramAction   
            key={order_id} // 用order_id作为key,确保相同id的组件复用
            onMouseEnter={getDebouncedEnter(order_id)}
            onMouseLeave={debouncedLeave} 
          />
        );
      })}
    </Box>
  );
};

方案二:在防抖函数内判断状态是否需要更新

在防抖逻辑中新增判断,只有当order_id与当前currentOrderId不同时才更新状态,避免父组件无意义渲染,同时用useCallback稳定函数引用。

import { useState, useCallback } from 'react';
import debounce from 'lodash/debounce';

const ParentComponent = () => {
  const [currentOrderId, setCurOrderId] = useState(null);

  const handleMouseEnter = useCallback(debounce((orderId) => {
    // 仅当order_id不同时才更新状态
    if (orderId !== currentOrderId) {
      setCurOrderId(orderId);
    }
  }, 300), [currentOrderId]);

  const handleMouseLeave = useCallback(debounce(() => {
    setCurOrderId(null);
  }, 300), []);

  return (
    <Box>
      {data.map(item => {
        const { order_id } = item;
        return (
          <GanttDiagramAction   
            key={order_id}
            onMouseEnter={() => handleMouseEnter(order_id)}
            onMouseLeave={handleMouseLeave} 
          />
        );
      })}
    </Box>
  );
};

额外优化:子组件使用memo

如果子组件GanttDiagramAction是纯组件,用memo包裹后,当props引用未变化时会跳过渲染,进一步提升性能:

import { memo } from 'react';

const GanttDiagramAction = memo(({ onMouseEnter, onMouseLeave }) => {
  // 子组件内部逻辑
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 23:27:45