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]);

1 Answer 1

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>
  );
};
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.