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

React与D3的useEffect中刷选交互触发无限循环的问题排查

修复React+D3刷选交互的无限循环问题

问题根源

你的代码里useEffect把pos放进了依赖数组,每次调用setPos更新pos,都会触发useEffect重新执行。而每次执行useEffect又会重新创建brush实例并绑定事件,刷选时再次触发setPos,形成无限循环。

修复步骤

  • 调整useEffect的依赖数组:把依赖改成空数组[],让useEffect只在组件挂载时执行一次。初始化坐标轴、创建brush这些操作不需要重复执行。
  • 缓存D3比例尺:用useMemo缓存x比例尺,避免每次组件渲染都重新创建实例,提升性能。
  • 避免不必要的状态更新:在brush事件里,先对比新的index和当前pos是否一致,一致就不调用setPos,减少无用重渲染。

修改后的代码

import { useState, useRef, useEffect, useMemo } from 'react';
import { select, brushX, brushSelection, scaleLinear, axisBottom } from 'd3';

const Timeline = () => {
    const svgRef = useRef(null!);
    const brushRef = useRef(null!);
    // 用useMemo缓存比例尺,避免重复创建
    const x = useMemo(() => scaleLinear().domain([0, 1000]).range([10, 810]), []);
    // 初始化pos默认值,避免渲染时出现undefined
    const [pos, setPos] = useState([0, 100]);

    useEffect(() => {
        const svg = select(svgRef.current);

        // 初始化坐标轴
        svg.select('.x-axis')
            .attr('transform', `translate(0,${110})`)
            .call(axisBottom(x));

        const brush = brushX()
            .extent([
                [10, 10],
                [810, 110],
            ])
            .on('start brush end', function () {
                const nodeSelection = brushSelection(select(brushRef.current).node());
                if (!nodeSelection) return; // 处理刷选取消的边界情况
                const index = nodeSelection.map(x.invert);
                
                // 对比当前状态,避免无意义更新
                if (index[0] !== pos[0] || index[1] !== pos[1]) {
                    setPos(index);
                }
            });

        // 初始化brush的初始位置
        select(brushRef.current).call(brush).call(brush.move, [0, 100].map(x));

        // 清理函数:组件卸载时移除事件监听,防止内存泄漏
        return () => {
            select(brushRef.current).call(brush.on('start brush end', null));
        };
    }, []); // 依赖数组设为空,仅组件挂载时执行一次

    return (
        <svg ref={svgRef} width="1200" height={800}>
            <rect x={x(200)} y={10} width={400} height={100} fill={'blue'} />
            <g className="x-axis" />
            <g ref={brushRef} />
            {/* 给文本添加位置和数值格式化,避免重叠和显示异常 */}
            <text x={10} y={150}>{pos[0].toFixed(2)}-{pos[1].toFixed(2)}</text>
        </svg>
    );
};

export default Timeline;

额外说明

  • 新增清理函数,组件卸载时移除brush的事件监听,避免内存泄漏。
  • 给pos设置初始值,解决渲染阶段文本显示undefined的问题。
  • 调整文本元素的位置并格式化数值,提升显示效果和可读性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 10:45:31