Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

D3 chart is duplicating on React.js component

This is on a react application, what might be going wrong? A new svg is appended everytime, instead of the one that exists updating…
I’m not sure what other details I should add, I already included all the info that you may need to help me, please let me know if there’s anything else I should add. Thank you.
enter image description here

This is my component, I’m passing the data through props.

export const FourDirectionsTimeChart = ({data}) => {
    useEffect(() => {
        const width = document.getElementById("container").clientWidth;
        const height = document.getElementById("container").clientHeight;
        const R = (width + height) / 8;
        const CX = width / 2;
        const CY = height / 2;
        const smallR = R * 0.1;
        const circleColor = "bisque";
        const itemColor = "#3F4200";

        let svg = d3.select("#container")
            .append("svg")
            .attr("preserveAspectRatio", "xMinYMin meet")
            .attr("viewBox", `0 0 ${width} ${height}`)

        let mainCircle = svg.append("circle")
            .attr("fill", circleColor)
            .attr("r", R)
            .attr("cx", CX)
            .attr("cy", CY);

        let centerCircle = svg.append("circle")
            .attr("fill", "white")
            .attr("r", smallR)
            .attr("cx", CX)
            .attr("cy", CY);

        const timePercentage = (time) => {
            const percentage = (time * 100 / 23) / 100;
            return percentage;
        };
        function timeToRadius(time) {
            return (smallR + timePercentage(time) * (R - smallR))
        }

        // Times concentric circles ---
        for (let i = 0; i <= 23; i += 4) {
            svg.append("circle")
                .attr("fill", "none")
                .attr("cx", CX)
                .attr("cy", CY)
                .attr("stroke-dasharray", "4 20")
                .attr("stroke", "gray")
                .attr("stroke-width", 1)
                .attr("r", timeToRadius(i))
        }

        // Cardinal points ---
        const textTime = 25;
        const fontSize = R * 0.25;
        svg.append("text")
            .attr("dx", getPosition(0, textTime).x)
            .attr("dy", getPosition(0, textTime).y)
            .text("N")
            .attr("fill", "black")
            .attr("font-size", fontSize)
            .attr("text-anchor", "middle")
            .style("font-family", "serif")
        svg.append("text")
            .attr("dx", getPosition(180, textTime).x)
            .attr("dy", getPosition(180, textTime).y)
            .text("S")
            .attr("fill", "black")
            .attr("font-size", fontSize)
            .attr("text-anchor", "middle")
            .style("font-family", "serif")
            .attr("alignment-baseline", "hanging")
        svg.append("text")
            .attr("dx", getPosition(-90, textTime).x)
            .attr("dy", getPosition(-90, textTime).y)
            .text("E")
            .attr("fill", "black")
            .attr("font-size", fontSize)
            .attr("text-anchor", "start")
            .attr("alignment-baseline", "middle")
            .style("font-family", "serif");
        svg.append("text")
            .attr("dx", getPosition(90, textTime).x)
            .attr("dy", getPosition(90, textTime).y)
            .text("O")
            .attr("fill", "black")
            .attr("font-size", fontSize)
            .attr("text-anchor", "end")
            .attr("alignment-baseline", "middle")
            .style("font-family", "serif")

        // Ships positions --- 
        function getPosition(degrees, time) {
            const getRadians = (degrees) => degrees * Math.PI / 180 + Math.PI / 2;
            let x = (smallR + timePercentage(time) * (R - smallR)) * Math.cos(getRadians(degrees)) + CX;
            let y = (smallR + timePercentage(time) * (R - smallR)) * Math.sin(getRadians(degrees)) * -1 + CY;
            return { x, y };
        }

        // Data mapping ---
        let parsedData = [];
        (() => {
            data?.forEach((x) => {
                parsedData.push({
                    id: x.Patente,
                    course: x.Rumbo,
                    speed: x.Velocidad,
                    time: new Date(x.Fecha_gps).getHours()
                })
            });

            let position;
            parsedData.map(d => {
                position = getPosition(d.course, d.time);
                svg.append("circle")
                    .attr("fill", () => {
                        if (d.speed == 0) return "brown";
                        else return "brown";
                    })
                    .attr("r", R * 0.015)
                    .attr("cx", position.x)
                    .attr("cy", position.y)
                    .attr("opacity", 0.4);
            });
        })();

    }, [data]);

    return (
        <div id="container" style={{ width: '100%', height: '100%' }}></div>
    )
}

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

What do you think the line let svg = d3.select("#container").append("svg") does? It selects the element with ID "container" and appends a new SVG element to it.

You can essentially do one of two things:

  1. Select and remove all existing SVG elements in the container and then draw the chart from scratch on update: d3.select("#container svg").remove(), then d3.select("#container").append("svg");
  2. Split the initialization (append svg, set size, draw axes) logic from the logic that might need to be run multiple times (update the values) and make sure to call only the second part on update. This is more complex, but also efficient, especially in an already DOM-heavy ecosystem like React.
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading