Search code examples
javascriptreactjsevent-listener

React Invalid Hook Call (Nested Hook?)


my goal here is to re-render map generative part again if localStorage data (a json of Layers LayersData) changes. React throws error of

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

That is my code file:

import React, { useRef, useEffect, useState } from 'react';
import Map from '@arcgis/core/Map.js';
import SceneView from '@arcgis/core/views/SceneView.js';
import TileLayer from '@arcgis/core/layers/TileLayer.js';
import esriConfig from "@arcgis/core/config.js";
import IntegratedMeshLayer from '@arcgis/core/layers/IntegratedMeshLayer.js';
import SceneLayer from '@arcgis/core/layers/SceneLayer.js';
import ElevationLayer from '@arcgis/core/layers/ElevationLayer.js';
import FeatureLayer from '@arcgis/core/layers/FeatureLayer.js';
import GroupLayer from '@arcgis/core/layers/GroupLayer.js';
import BaseMap from '@arcgis/core/BaseMap.js';
import LayerList from "@arcgis/core/widgets/LayerList.js";

/* const LayersData =JSON.parse(localStorage.getItem('fakelayers')); */
const LayersData = useLayerData();

function sublayerVisibility(layername, sublayername, jsonobject){
  var visibility = null;
  const sublayerData = jsonobject.layers.find(layer => layer.layer === layername);
        if (sublayerData) {
        visibility = sublayerData.sublayers[sublayername];}
        else{
        visibility = false;
        }
        return visibility;
}

// uploading json to localStorage
function getLayerData() {
  return JSON.parse(localStorage.getItem('fakelayers'));
}

//for updating data
function useLayerData(){
  const [LayersData, setLayer] = useState(getLayerData());

  useEffect(() => {
    function handleChangeStorage(){
      setLayer(getLayerData());
    }
    window.addEventListener('storage',handleChangeStorage);
    return () => {
      window.removeEventListener('storage',handleChangeStorage);
    }
  },[]);
    return LayersData;
}

esriConfig.apiKey = "MYAPIKEY"
// Part about the map creation
function MapComponentv2() {
  const sceneViewRef = useRef(null);
  const layerListRef = useRef(null);
  const [view, setView] = useState(null);
  

  useEffect(() => {
    const initialMap = new Map({
      basemap: "hybrid",
      listMode: 'hide'
    });

    const sceneView = new SceneView({
      container: sceneViewRef.current,
      map: initialMap,
      camera: {
        tilt: 45,
        heading: 0,
        position: {
          latitude: 21.47840,
          longitude: 39.18526,
          z: 650,
        }
      },
      environment: {
        atmosphere: {
          quality: "high"
        },
        lighting: {
          date: new Date(),
          directShadowsEnabled: true
        }
      },
    });

    setView(sceneView);

    return () => {
      sceneView.container = null;
      sceneView.destroy();
    };   
  }, []);

  useEffect(() => {
    if (view ) {
      view.when(() => {
        // View is fully loaded here
        // Add your 3D layers or other configurations
        //... some more layers etc
        const City3DGroup = new GroupLayer({
          layers:[buildingsHigh,test],
          title: "3D City Mash",
          visible : sublayerVisibility("3D Data, Applications", "3D City Mesh", LayersData)
        
        });
         
       
        if (view.map) {
          view.map.addMany([City3DGroup]);
        }
        

        
      });
    }
  }, [view, LayersData]);

  const containerStyle = {
    width: '100%',
    height: '100%',
    position: 'fixed',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
  };

  return (
  <div>
    <div id="map" ref={sceneViewRef} style={containerStyle}></div>
    
  </div>
    
  );
}

export default MapComponentv2;


Solution

  • You can move the useLayerData hook call inside the MapComponentv2 functional component.

    function MapComponentv2() {
      const LayersData = useLayerData();
      // ... rest of your code
    }