Home > Software design >  Cannot do a brush interaction and set a state in a useEffect hook with D3 and React
Cannot do a brush interaction and set a state in a useEffect hook with D3 and React

Time:08-08

I'm trying to create a brush interaction on a timeline with D3 and React but I can't figure out why it doesn't work.

Everything seems fine but when I set a state within the useEffect() the index variable creates an infinite loop.

    const Timeline = () => {
    const svgRef = useRef(null!);
    const brushRef = useRef(null!);
    const x = scaleLinear().domain([0, 1000]).range([10, 810]);
    const [pos, setPos] = useState([]);

    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(),
                );
                const index = nodeSelection.map(x.invert);

                setPos(index);
            });

        console.log(pos);

        select(brushRef.current).call(brush).call(brush.move, [0, 100].map(x));
    }, [pos]);

    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>{pos[0]}-{pos[1]}</text>
        </svg>
    );
};

Any ideas to fix that? Thanks!

CodePudding user response:

Seems that this solves the problem:

const runBrushes = useCallback(async () => {
    const brush = brushX()
        .extent([
            [10, 10],
            [810, 110],
        ])
        .on('start brush end', function () {
            const nodeSelection = brushSelection(
                select(brushRef.current).node(),
            );
            const index = nodeSelection.map(x.invert);

            setPos(index);
        });

    select(brushRef.current).call(brush).call(brush.move, [0, 100].map(x));
}, [pos, x]);

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

    svg.select('.x-axis')
        .attr('transform', `translate(0,${110})`)
        .call(axisBottom(x));

    runBrushes().catch(console.error);
}, []);
  • Related