reactjsreact-hooksesriarcgis-js-api

How to make dynamic changes in Arcgis JS map-component with React?


I am quite new to Arcgis-JS and React. As suggested here, I am using a functional component with the useEffect hook to integrate my map.

Now I want to display a line within my map when I click on a certain row of a list. On click I am fetching the appropriate coordinates to be displayed and storing them to a context-variable (dbPageContext.currentGeom).

The problem: When I want to display another line, the entire map-component has to re-render as I am passing the line-array-variable as a second argument to the useEffect hook.

const MapComp = () => {
  const mapRef = useRef();
  const dbPageContext = useContext(DbPageContext);


  useEffect(() => {
    const mainMap = new Map({
      layers: [layer],
      basemap: "arcgis-topographic", // Basemap layer service
    });

    const graphicsLayer = new GraphicsLayer();
    mainMap.add(graphicsLayer);

    const polyline = {
      type: "polyline",
      paths: dbPageContext.currentGeom, 
    };
    const simpleLineSymbol = {
      type: "simple-line",
      color: [0, 230, 250], 
      width: 4,
    };

    const polylineGraphic = new Graphic({
      geometry: polyline,
      symbol: simpleLineSymbol,
    });
    graphicsLayer.add(polylineGraphic);

    const view = new MapView({
      container: mapRef.current,
      map: mainMap,
      spatialReference: {
        wkid: 3857,
      },
    });
    return () => {
      view && view.destroy();
    };
  }, [dbPageContext.currentGeom]);

  return (
    <div>
      <div className="webmap" ref={mapRef} />
    </div>
  );
};

export default MapComp;

How can I update only the graphics-layer without updating the entire map-component? Would be great if someone could help me finding a solution for that.

EDIT: I also tried to implement the map without using the useeffect hook. But then, nothing was displayed.


Solution

  • You need to separate the effects. On page load, you should have one effect that creates the map. Then a second effect can update the map when dbPageContext.currentGeom changes.

    const MapComp = () => {
      const mapRef = useRef();
      const dbPageContext = useContext(DbPageContext);
    
      // Memoize this, as you only need to create it once, but you also need
      // it to be available within scope of both of the following useEffects
      const graphicsLayer = React.useMemo(
        () => new GraphicsLayer(), 
        []
      );
    
      // On mount, create the map, view, and teardown
      useEffect(() => {
        const mainMap = new Map({
          layers: [layer],
          basemap: "arcgis-topographic", // Basemap layer service
        });
    
        const view = new MapView({
          container: mapRef.current,
          map: mainMap,
          spatialReference: {
            wkid: 3857,
          },
        });
    
        mainMap.add(graphicsLayer);
    
        return () => {
          view && view.destroy();
        };
      }, [])
    
      // When dbPageContext.currentGeom changes, add a polyline
      // to the graphics layer
      useEffect(() => {
        const polyline = {
          type: "polyline",
          paths: dbPageContext.currentGeom, 
        };
        const simpleLineSymbol = {
          type: "simple-line",
          color: [0, 230, 250], 
          width: 4,
        };
    
        const polylineGraphic = new Graphic({
          geometry: polyline,
          symbol: simpleLineSymbol,
        });
     
        // Clear previously added lines (if that's what you want)
        graphicsLayer.removeAll()
        graphicsLayer.add(polylineGraphic);
      }, [dbPageContext.currentGeom]);
    
      return (
        <div>
          <div className="webmap" ref={mapRef} />
        </div>
      );
    };
    
    export default MapComp;