1

How do I access the updated useState value inside a Openlayers event listener callback function?

select.getFeatures().on(['add'], selectAddCallback);

Example here: https://stackblitz.com/edit/react-ts-zs4uvs?file=Map.tsx

Click the button and it will add an item to the list. I then use useEffect to unset and reset the callback function but the useState list is still the original empty list, not the updated list

  useEffect(() => {
     select.getFeatures().un(['add'], selectAddCallback);
     select.getFeatures().on(['add'], selectAddCallback);
  }, [mapLayers]);
Angus Ross
  • 11
  • 1

1 Answers1

0

You are recreating the Select interaction component on each render, this is why you lose the handlers. Moving it out of the component fixes your problem.

I also suggest you take a look at https://github.com/mmomtchev/rlayers (of which I am the author) instead of trying to reinvent the wheel. I already spent many hours fixing such problems.

import React, { useRef, useState, useEffect } from 'react';
import Map from 'ol/Map';
import View from 'ol/View';
import OlLayerTile from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';
import Select from 'ol/interaction/Select';
import OlGeoJSON from 'ol/format/GeoJSON';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import './style.css';

const initLayers = [
  new OlLayerTile({
    source: new XYZ({
      url: 'http://mt0.google.com/vt/lyrs=s&hl=en&x={x}&y={y}&z={z}',
    }),
  }),
];

const select = new Select({
  hitTolerance: 4,
});

export default () => {
  const mapElement = useRef<HTMLDivElement | null>(null);
  const [map, setMap] = useState<Map | undefined>();
  const [mapLayers, setMapLayers] = useState<string[]>([]);

  useEffect(() => {
    const newMap = initMap(mapElement);

    newMap.addInteraction(select);

    const geojson = {
      type: 'Polygon',
      coordinates: [
        [
          [30.0, 10.0],
          [40.0, 40.0],
          [20.0, 40.0],
          [10.0, 20.0],
          [30.0, 10.0],
        ],
      ],
    };
    let format = new OlGeoJSON();
    let features = format.readFeatures(geojson);
    let source = new VectorSource({
      features: features,
    });
    let layer = new VectorLayer({
      source: source,
    });

    newMap.addLayer(layer);

    setMap(newMap);
  }, []);
  function initMap(
    mapElement: React.MutableRefObject<HTMLDivElement | null>
  ): Map | undefined {
    if (!mapElement.current) return;

    const view = new View({
      projection: 'EPSG:4326',
      center: [30.0, 10.0],
      zoom: 3,
    });

    const map = new Map({
      layers: initLayers,
      view: view,
      controls: [],
    });
    map.setTarget(mapElement.current);
    return map;
  }
  useEffect(() => {
    console.log('Map Layer Change:', mapLayers);
    select.getFeatures().un(['add'], selectAddCallback);
    select.getFeatures().on(['add'], selectAddCallback);
  }, [mapLayers]);

  const handleClick = () => {
    setMapLayers((prevList) => [...prevList, 'New Item']);
  };

  const selectAddCallback = () => {
    console.log('Select Callback');
    console.log(mapLayers);
  };
  return (
    <div className="container">
      <div id="Map-provider" ref={mapElement} className="map" />
      <button className="button" onClick={handleClick}>
        Add
      </button>
    </div>
  );
};
mmomtchev
  • 2,497
  • 1
  • 8
  • 23