如何阻止 D3 图表在 React 应用程序中首次渲染时重复?

问题描述 投票:0回答:4

由于某种原因,我的 D3 图表仅在第一次渲染时重复。它将在第一个图表的下方垂直堆叠第二个图表。

但是当我点击刷新时,它再也不会重复了。有没有办法让 .forEach 循环一次或设置某种中断或条件以防止第一次重复渲染?

或者我需要修改useEffect()吗?


export default function BarChart() {

    useEffect(() => {

        db.collection('revenue').get().then(res => { 
            let data = [];
            res.docs.forEach(doc => {   
                data.push(doc.data()); 
            });
      console.log(data);

            const svg = d3.select('.canvas')
                .append('svg')
                .attr('width', '640')
                .attr('height', '400');


            const margin = { top: 20, right: 20, bottom: 100, left: 100 };
            const graphWidth = 600 - margin.left - margin.right;
            const graphHeight = 400 - margin.top - margin.bottom;

            const graph = svg.append('g')
                .attr('width', graphWidth)
                .attr('height', graphHeight)
                .attr('transform', `translate(${margin.left}, ${margin.top})`);

            // create axes groups
            const xAxisGroup = graph.append('g')
                .attr('transform', `translate(0, ${graphHeight})`)

            const yAxisGroup = graph.append('g');

            const y = d3.scaleLinear()
                .domain([0, d3.max(data, d => d.amount)])
                .range([graphHeight, 0]);

            const x = d3.scaleBand()
                .domain(data.map(item => item.date))
                .range([0, graphWidth])
                .paddingInner(0.2)
                .paddingOuter(0.2);

            // join the data to circs
            const rects = graph.selectAll('rect')
                .data(data);

            // add attrs to circs already in the DOM
            rects.attr('width', '50')
                .attr("height", d => graphHeight - y(d.amount))
                .attr('fill', 'Red')
                .attr('x', d => x(d.date))
                .attr('y', d => y(d.amount));

            // append the enter selection to the DOM
            rects.enter()
                .append('rect')
                .attr('width', '50')
                .attr("height", 0)
                .attr('fill', 'Red')
                .attr('id', d => (d.id))
                .attr('x', (d) => x(d.date))
                .attr('y', graphHeight)
                .transition().duration(500)
                 .attr('y', d => y(d.amount))
                 .attr("height", d => graphHeight - y(d.amount))

            const xAxis = d3.axisBottom(x);
            const yAxis = d3.axisLeft(y)
                .ticks(4)
                .tickFormat(d => d + ' dollars');

            xAxisGroup.call(xAxis);
            yAxisGroup.call(yAxis);

            // Bottom Chart Text
            xAxisGroup.selectAll('text')
                .attr('fill', 'Teal')
                //.attr('font-weight', 'bold')
                .attr('transform', 'rotate(-40)')
                .attr('text-anchor', 'end')
        });

    }, []); // Array for data

    return (
        <div>
            <h1">Title</h1>
        </div>
    );

}

javascript reactjs loops d3.js
4个回答
4
投票

您可以尝试向组件添加 SVG


    return (
        <div>
            <h1">Title</h1>
            <svg className="svg-canvas" width="640px" height="400px" />
        </div>
    );

并改变

const svg = d3.select('.canvas')
  .append('svg')
  .attr('width', '640')
  .attr('height', '400');

//to

const svg = d3.select('.svg-canvas')
svg.selectAll("*").remove()

这将确保 SVG 在重新渲染之前始终为空。不过,如果您的数据更新,它将重新渲染所有内容。


1
投票

你可以试试这个:

export function component_name {data, ...props} {
    const yourRef = useRef()
    useEffect(() => {
        /*All your code here*/
        yourRef.current.append(X) /*"X" is your first SVG element that contains the others*/
        return() => X.remove()

    }, [data])}

无论您在哪里调用组件:

<component_name data={data} />

只要您的数据发生变化,您的组件就会被删除。


0
投票

我简单地这样做:

  // UseEffect
     React.useEffect(()=> {
          // Hide first svg
     let svg = document.getElementsByTagName('svg');
        // If data present create chart
        if(data){
        // Create chart
       D3Chart(chartDiv.current, data);

// Hide first empty svg
       svg[0].style.display='none';
         }
     },[data]);

0
投票

有点晚了,但我来了,他

还要检查您的应用程序中是否启用了 StrictMode,因为它会渲染两倍的包装树组件。

© www.soinside.com 2019 - 2024. All rights reserved.