React-map-gl with axios - Error "Can't perform a React state update on an unmounted component"

318 Views Asked by At

I have been on this simple file for 3 days and need your help. I'm new to React, and MapBox too :).

I need to consume an api and display it on a mapbox. Everything works fine with a JSON (ANNONCE) but with useEffect(axios-asynchronous), I have an error of type: "Can't perform a React state update on an unmounted component" .I think it's a timing error (componentDidMount maybe) but i don't know how solve it. Could you help me? Thank you very much.

    import "mapbox-gl/dist/mapbox-gl.css";
import "react-map-gl-geocoder/dist/mapbox-gl-geocoder.css";
import React, {PureComponent, useCallback, useEffect, useRef, useState, } from "react";
import {render} from "react-dom";
import MapGL, {Marker} from "react-map-gl";
import Geocoder from "react-map-gl-geocoder";
import axios from 'axios'


const MAPBOX_TOKEN =
  '';

const ANNONCE = [
  {
    "@id": "/api/announces/12",
    "@type": "Announce",
    "subject": "Sujet essaicatogrie",
    "duration": 3,
    "gratis": true,
    "lat": 48.17,
    "lon": 4.23

  },
  {
    "@id": "/api/announces/13",
    "@type": "Announce",
    "subject": "Marche sur les terrils",
    "duration": 2,
    "gratis": true,
    "lat": 47.02,
    "lon": -0.71
  },
  {
    "@id": "/api/announces/14",
    "@type": "Announce",
    "subject": "footing en montagne",
    "duration": 2,
    "gratis": false,
    "lat": 46.27,
    "lon": 3.67
  },
  {
    "@id": "/api/announces/15",
    "@type": "Announce",
    "subject": "Kayak sur la loire",
    "duration": 4,
    "gratis": false,
    "lat": 44.45,
    "lon": 1.06
  }
]
class Markers extends PureComponent {
  render() {
    const {data} = this.props;

    return data.map(
      city =>
        <Marker key={city.id} longitude={city.lon} latitude={city.lat}>
          <img src='http://placekitten.com/200/300'/>
        </Marker>
    )
  }
}
/* *********************************************************/
const useDataApi = (initialUrl, initialData) => {
  const [data, setData] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [items, setItems] = useState([])
  const [count ,setCount]= useState(0)
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  const isMountedRef = useRef(null);


  useEffect(() => {
    isMountedRef.current = true;

    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);

      try {
        const result = await axios(url);

        setData(result.data);
        setItems(result.data['hydra:member'])
        setCount(result.data['hydra:totalItems'])

      } catch (error) {
        setIsError(true);

      }

      setIsLoading(false);
    };

    fetchData();
  }, [url]);

  return [{ items,count,data, isLoading, isError }, setUrl];
};

/* *********************************************************/

const App = () => {
  const [query, setQuery] = useState('');

  const [{ items,count,data, isLoading, isError }, doFetch] = useDataApi(
    '/api/announces/',
    { hits: [] },
  );
  const [viewport, setViewport] = useState({
    latitude: 46.5,
    longitude: 2.3,
    zoom: 5
  });

  const mapRef = useRef();
  const handleViewportChange = useCallback(
    (newViewport) => setViewport(newViewport),
    []
  );


  /************************  GEOCODER **********************/
  const handleGeocoderViewportChange = useCallback(
    (newViewport) => {
      const geocoderDefaultOverrides = {transitionDuration: 1000};

      return handleViewportChange({
        ...newViewport,
        ...geocoderDefaultOverrides
      });
    },
    [handleViewportChange]
  );
  /***************************************************/

  return (
    <div className="map-wrapper">
      <MapGL
        ref={mapRef}
        {...viewport}
        width="100%"
        height="100%"
        onViewportChange={handleViewportChange}
        mapStyle="mapbox://styles/mapbox/streets-v11"
        mapboxApiAccessToken={MAPBOX_TOKEN}>

        <Geocoder
          mapRef={mapRef}
          onViewportChange={handleGeocoderViewportChange}
          mapboxApiAccessToken={MAPBOX_TOKEN}
          position="top-left"
        />
        <Markers data={items}  // <------ I NEED TO CHANGE THIS IN "items" (axios - api)

        />
      </MapGL>
    </div>
  );
};

render(<App/>, document.getElementById("idmap"));
1

There are 1 best solutions below

0
On

I encountered the same issue. The problem was that the latitude and longitude values coming from the API were represented as strings. Passing these string coordinates to the Marker component caused the initial error, which then resulted in a cascading series of confusing messages.

So while the final error stated that you cannot update state on an unmounted component, the real cause was a simple type error.