React component that programmatically renders Points

543
0
01-31-2023 02:20 PM
scubakobe
New Contributor

Hello,

Hoping someone has some advice for how to approach this problem. Let me start with the desired design:

  • Display the Map with a Sketch widget to allow the user to draw a line
  • An external component allows the user to select the desired point from external state variable mapState that is passed in as a prop using Redux
    • The selected point from mapState should be displayed as a simple marker on the map

I've tried several approaches for this. I initially started prototyping the React component using loadModules but this wasn't very easy to manage when I needed to expose the ESRI modules in several useEffect hooks.

What I'm having trouble with is that the points do not get displayed on the map. It's as if the view does not know it needs to update. 

I then tried moving the MapView to a state variable with useState but it was difficult to then manage adding Points to the GraphicsLayer because the spread operator would need to be used to deep clone the modified object back into the state variable, since view.graphics.add(newPoint) would not update the view state. I tried using the spread operator (...) but even this did not trigger the map to re-render with the point visible.

I've also tried view.goTo(newPoint) to force a re-render but this causes the entire map area to go blank.

Are there any React examples out there of a marker being dynamically updated on the map?

Approach 1:

 

import Map from "@arcgis/core/Map";
import MapView from "@arcgis/core/views/MapView";
import Sketch from "@arcgis/core/widgets/Sketch";
import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer";
import Graphic from "@arcgis/core/Graphic";
import Point from "@arcgis/core/geometry/Point";
import * as webMercatorUtils from "@arcgis/core/geometry/support/webMercatorUtils";

const WebMapView = ({ setWxState, mapState }) => {
  const mapRef = useRef();
  const [view, setView] = useState();

  const simpleMarkerSymbol = {
    type: "simple-marker",
    color: [226, 119, 40], // Orange
    outline: {
      color: [255, 255, 255], // White
      width: 1,
    },
  };

  useEffect(() => {
    if (mapState?.payload) {
      const newLayer = new GraphicsLayer();
      const point = new Point({
        //Create a point
        longitude: mapState?.payload?.lon,
        latitude: mapState?.payload?.lat,
      });

      const newPoint = new Graphic({
        geometry: point,
        symbol: simpleMarkerSymbol,
      });
      newLayer.add(newPoint);
      // Method 1
      // map.add(newLayer);

      // Method 2
      view.graphics.add(newPoint);
      setView({
        ...view,
        graphics: {
          ...view.graphics,
          items: [...view.graphics.items],
        },
      });
    }
  }, [mapState]);

  useEffect(() => {
    const map = new Map({
      basemap: "topo-vector",
      ground: "world-elevation",
    });

    const handleDraw = async (event) => {
      if (event.state === "complete" && event.tool === "polyline") {
        const convertedGeo = webMercatorUtils.webMercatorToGeographic(
          event.graphic.geometry
        );
        const points = convertedGeo.paths[0];
        // Business logic
      }
    };

    // load the map view at the ref's DOM node
    const newView = new MapView({
      container: mapRef.current,
      map: map,
      center: [-118, 34],
      zoom: 8,
    });

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

    newView.when(() => {
      // Create a new sketch widget
      const sketch = new Sketch({
        view: newView,
        layer: graphicsLayer,
        polylineSymbol: {
          type: "simple-line", // autocasts as new SimpleLineSymbol()
          color: [4, 90, 141],
          width: 2,
          // creationMode: "update"
        },
      });

      // Add the sketch widget to the view
      newView.ui.add(sketch, "top-right");
      sketch.on("create", handleDraw);
      sketch.on("update", handleDraw);
    });
    setView(newView);

    return () => {
      if (view) {
        // destroy the map view
        view.destroy();
      }
    };
  }, []);

  return <div style={{ position: "relative", height: "60%" }} ref={mapRef} />;
};

 

 

0 Kudos
0 Replies