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!
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);
}, []);