using three js, next js, react and globe.gl documentation, ensure that the following three globe is cleaned up correctly fyi globe.dispose() is not a function in any of the libraries concerned hence the...use documentation instruction.: import React, { useEffect, useRef } from 'react';
import {
    WebGLRenderer,
    Scene,
    AmbientLight,
    DirectionalLight,
    Color,
    Fog,
    PerspectiveCamera,
    PointLight
} from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { createGlowMesh } from 'three-glow-mesh';
import ThreeGlobe from "three-globe";
import countries from './files/globe-data-min.json';
import travelHistory from './files/my-flights.json';
import airportHistory from './files/my-airports.json';

let mouseX = 0;
let mouseY = 0;

let timeoutId;
let renderer, camera, scene, controls;
let Globe;
let frameId;


const GlobeComponent = ({ globeWidth, globeHeight, windowWidth, windowHeight }) => {
    const containerRef = useRef();
    let windowHalfX = windowWidth / 2;
    let windowHalfY = windowHeight / 2;


    // Event listeners
    function onWindowResize() {
        camera.aspect = windowWidth / windowHeight;
        camera.updateProjectionMatrix();
        windowHalfX = windowWidth;
        windowHalfY = windowHeight;
        renderer.setSize(windowWidth, windowHeight);
    }

    function onMouseMove(event) {
        mouseX = event.clientX - windowHalfX;
        mouseY = event.clientY - windowHalfY;
        // console.log("x: " + mouseX + " y: " + mouseY);
    }

    // Animation
    function animate() {
        camera.lookAt(scene.position);
        controls.update();
        renderer.render(scene, camera);
        frameId = requestAnimationFrame(animate);
    }

    useEffect(() => {
        // Initialize core ThreeJS elements
        function init() {
            // Initialize renderer
            renderer = new WebGLRenderer({ antialias: true, alpha: true });
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(globeWidth, globeHeight);
            renderer.setClearColor(0x000000, 0);
            containerRef.current.appendChild(renderer.domElement);

            // Initialize scene, light
            scene = new Scene();
            scene.add(new AmbientLight(0xbbbbbb, 0.4));

            // Initialize camera, light
            camera = new PerspectiveCamera();
            camera.aspect = globeWidth / globeHeight;
            camera.updateProjectionMatrix();

            var dLight = new DirectionalLight(0xffffff, 0.8);
            dLight.position.set(-800, 2000, 400);
            camera.add(dLight);

            var dLight1 = new DirectionalLight(0x7982f6, 1);
            dLight1.position.set(-200, 500, 200);
            camera.add(dLight1);

            var dLight2 = new PointLight(0x8566cc, 0.5);
            dLight2.position.set(-200, 500, 200);
            camera.add(dLight2);

            camera.position.z = 400;
            camera.position.x = 0;
            camera.position.y = 0;

            scene.add(camera);

            // Additional effects
            scene.fog = new Fog(0x535ef3, 400, 2000);

            // Helpers
            // const axesHelper = new THREE.AxesHelper(800);
            // scene.add(axesHelper);
            // var helper = new THREE.DirectionalLightHelper(dLight);
            // scene.add(helper);
            // var helperCamera = new THREE.CameraHelper(dLight.shadow.camera);
            // scene.add(helperCamera);

            // Initialize controls
            controls = new OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;
            controls.dynamicDampingFactor = 0.01;
            controls.enablePan = false;
            controls.minDistance = Math.min(globeWidth, globeHeight) / 2;
            controls.maxDistance = Math.min(globeWidth, globeHeight) / 2;
            controls.rotateSpeed = 0.8;
            controls.zoomSpeed = 1;
            controls.autoRotate = false;

            controls.minPolarAngle = Math.PI / 3.5;
            controls.maxPolarAngle = Math.PI - Math.PI / 3;

        }

        // Initialize the Globe
        function initGlobe() {
            // Initialize the Globe
            Globe = new ThreeGlobe({
                waitForGlobeReady: true,
                animateIn: true,
            })
                .hexPolygonsData(countries.features)
                .hexPolygonResolution(3)
                .hexPolygonMargin(0.7)
                .showAtmosphere(true)
                .atmosphereColor("#ffffff")
                .atmosphereAltitude(0.1)
                .hexPolygonColor((e) => {
                    if (
                        ["KEN", "CHN", "FRA", "ZAF", "JPN", "USA", "AUS", "CAN"].includes(
                            e.properties.ISO_A3
                        )
                    ) {
                        return "rgba(255,255,255, 1)";
                    } else return "rgba(255,255,255, 0.5)";
                });

            // NOTE Arc animations are followed after the globe enters the scene
            timeoutId = setTimeout(() => {
                Globe.arcsData(travelHistory.flights)
                    .arcColor((e) => {
                        return e.status ? "#9cff00" : "#ff2e97";
                    })
                    .arcAltitude((e) => {
                        return e.arcAlt;
                    })
                    .arcStroke((e) => {
                        return e.status ? 0.5 : 0.3;
                    })
                    .arcDashLength(0.9)
                    .arcDashGap(4)
                    .arcDashAnimateTime(1000)
                    .arcsTransitionDuration(1000)
                    .arcDashInitialGap((e) => e.order * 1)
                    .labelsData(airportHistory.airports)
                    .labelColor(() => "#ffffff")
                    .labelDotOrientation((e) => {
                        return e.text === "NGA" ? "top" : "right";
                    })
                    .labelDotRadius(0.35)
                    .labelSize((e) => e.size)
                    .labelText("city")
                    .labelResolution(6)
                    .labelAltitude(0.01)
                    .pointsData(airportHistory.airports)
                    .pointColor(() => "#ffffff")
                    .pointsMerge(true)
                    .pointAltitude(0.07)
                    .pointRadius(0.10);
            }, 1000);

            Globe.rotateX(-Math.PI * (1 / 50));
            Globe.rotateY(-Math.PI * (1 / 9));
            Globe.rotateZ(-Math.PI / 60);
            const globeMaterial = Globe.globeMaterial();
            globeMaterial.color = new Color(0x3a228a);
            globeMaterial.emissive = new Color(0x220038);
            globeMaterial.emissiveIntensity = 0.1;
            globeMaterial.shininess = 0.7;

            // NOTE Cool stuff
            // globeMaterial.wireframe = true;

            scene.add(Globe);
        }

        init();
        initGlobe();
        onWindowResize();
        animate();

        window.addEventListener('resize', onWindowResize, false);
        renderer.domElement.addEventListener('mousemove', onMouseMove);

        console.log("Initializing globe animation");


        return () => {
            clearTimeout(timeoutId);
            window.removeEventListener('resize', onWindowResize);
            renderer.domElement.removeEventListener('mousemove', onMouseMove);
            cancelAnimationFrame(frameId);

            if (containerRef.current) {
                // Perform more thorough clean-up here

                // Dispose of the scene's children
                while (scene.children.length > 0) {
                    const object = scene.children[0];
                    if (object.dispose) {
                        object.dispose();
                    }
                    scene.remove(object);
                }

                // Dispose of the renderer and any associated resources
                renderer.dispose();

                // Dispose of any controls, if necessary
                if (controls && controls instanceof OrbitControls && controls.dispose) {
                    controls.dispose();
                }

                if (renderer.domElement.parentNode === containerRef.current) {
                    // Remove the renderer DOM element
                    containerRef.current.removeChild(renderer.domElement);
                }

            }
        };
    }, []);

    return <div ref={containerRef} />;

};

export default GlobeComponent;