vasturiano / react-globe.gl

React component for Globe Data Visualization using ThreeJS/WebGL
https://vasturiano.github.io/react-globe.gl/example/world-population/
MIT License
866 stars 155 forks source link

Prevent re-rendering #171

Open kamami opened 6 months ago

kamami commented 6 months ago

I am using an interval to change the population of my HexTubes and make an animation happen. I also have some Arcs added to the globe. Now I noticed, that whenever I update popData in the interval the arcs disappear and reappear again -> The whole globe is re-rendering. How can I prevent this behavior?

"use client";
import { useRef, useEffect, useState } from "react";
import Globe from "react-globe.gl";

export default function World() {
  const globeEl = useRef();
  const [countries, setCountries] = useState({ features: [] });
  const [popData, setPopData] = useState([]);
  const [increasePop, setIncreasePop] = useState(true);

  const N = 20;
  const arcsData = [...Array(N).keys()].map(() => ({
    startLat: (Math.random() - 0.5) * 180,
    startLng: (Math.random() - 0.5) * 360,
    endLat: (Math.random() - 0.5) * 180,
    endLng: (Math.random() - 0.5) * 360,
    color: [
      ["red", "white", "blue", "green"][Math.round(Math.random() * 3)],
      ["red", "white", "blue", "green"][Math.round(Math.random() * 3)],
    ],
  }));

  useEffect(() => {
    fetch("ne_110m_admin_0_countries.geojson")
      .then((res) => res.json())
      .then(setCountries);

    setPopData([
      { lat: 49.0, lng: 10.0, pop: 800000, originalPop: 800000 },
      { lat: 49.5, lng: 10.8, pop: 700000, originalPop: 700000 },
      { lat: 49.5, lng: 9.3, pop: 680000, originalPop: 680000 },
      { lat: 49.0, lng: 9.0, pop: 650000, originalPop: 650000 },
      { lat: 48.5, lng: 9.5, pop: 630000, originalPop: 630000 },
      { lat: 48.5, lng: 10.5, pop: 600000, originalPop: 600000 },
      { lat: 49.0, lng: 11.0, pop: 520000, originalPop: 520000 },
      { lat: 49.5, lng: 11.5, pop: 440000, originalPop: 440000 },
      { lat: 49.5, lng: 8.5, pop: 360000, originalPop: 360000 },
      { lat: 49.0, lng: 8.0, pop: 280000, originalPop: 280000 },
      { lat: 48.5, lng: 8.5, pop: 200000, originalPop: 200000 },
      { lat: 48.5, lng: 11.5, pop: 120000, originalPop: 120000 },
      { lat: 49.0, lng: 12.0, pop: 50000, originalPop: 50000 },
    ]);
  }, []);

  useEffect(() => {
    // Auto-rotate
    globeEl.current.controls().enableZoom = false;
    globeEl.current.controls().autoRotate = true;
    globeEl.current.controls().autoRotateSpeed = 1;
  }, []);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setPopData((currentData) =>
        currentData.map((data) => {
          const zufaelligerFaktor = Math.random() * (2 - 1.1) + 1.1; // Erzeugt einen Wert zwischen 1.1 und 2
          return {
            ...data,
            pop: increasePop ? data.originalPop * zufaelligerFaktor : 0, // Multipliziere originalPop mit dem zufälligen Faktor
          };
        })
      );
      setIncreasePop((prevState) => !prevState); // Wechsle den Zustand bei jedem Interval
    }, 2000); // Wechsle alle 2 Sekunden

    return () => clearInterval(intervalId);
  }, [increasePop, popData]);

  return (
    <Globe
      ref={globeEl}
      width={window.innerWidth - 64}
      backgroundColor="#fff"
      atmosphereColor="#f0f0f0"
      atmosphereAltitude={0.5}
      showGlobe={true}
      globeImageUrl="white.jpg"
      hexPolygonsData={countries.features}
      hexPolygonResolution={3}
      hexPolygonMargin={0.4}
      hexPolygonUseDots={false}
      hexPolygonColor={(d) =>
        d.properties.REGION_UN == ("Americas" || "Europe") ? "#dbdbdb" : "#000"
      }
      hexBinResolution={3}
      hexMargin={0.4}
      hexBinPointsData={popData}
      hexBinPointWeight="pop"
      hexAltitude={(d) => d.sumWeight * 6e-8}
      hexTopColor="#000"
      hexSideColor="#000"
      hexTransitionDuration={500}
      arcsData={arcsData}
      arcColor={"color"}
      arcDashLength={() => Math.random()}
      arcDashGap={() => Math.random()}
      arcDashAnimateTime={() => Math.random() * 4000 + 500}
    />
  );
}

https://github.com/vasturiano/react-globe.gl/assets/43880037/1de9137f-c9f5-4a54-b2d4-a04f4fa0ca65

vasturiano commented 5 months ago

@kamami I think the culprit of the issue is that you should memoize these methods:

arcDashLength={() => Math.random()}
arcDashGap={() => Math.random()}
arcDashAnimateTime={() => Math.random() * 4000 + 500}

Otherwise everytime your component rerenders, you're essentially passing new method instances to globe, indicating that you wish that functionality to change and start over.