0

The problem is that every time I hover on a marker a popup is opened or closed and it causes all the markers to re-render even though my state is not changing. console.log(myState); is running every time I hover in and out of the marker.

I tried to use useMemo hook but couldn't figure out how to use it on country.map. Any help?

Here is my code:

import React, { useEffect, useState } from 'react';
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import axios from 'axios';
import { v4 as uuidv4 } from 'uuid';
import { Icon } from 'leaflet';

const myicon = new Icon({
  iconUrl: './icon.svg',
  iconSize: [20, 20]
});

const MyMap = () => {
  const [myState, setMyState] = useState(null);
  const [activePlace, setActivePlace] = useState(null);

  const getData = async () => {
    let response = await axios
      .get('https://corona.lmao.ninja/v2/jhucsse')
      .catch(err => console.log(err));

    let data = response.data;
    setMyState(data);

    // console.log(data);
  };

  useEffect(() => {
    getData();
  }, []);

  if (myState) {
    console.log(myState);
    return (
        <Map
          style={{ height: '100vh', width: '100vw' }}
          center={[14.561, 17.102]}
          zoom={1}
        >
          <TileLayer
            attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>'
            url={
              'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png'
            }
          />

          {myState.map(country => {
            return (
              <Marker
                key={uuidv4()}
                position={[
                  country.coordinates.latitude,
                  country.coordinates.longitude
                ]}
                onmouseover={() => {
                  setActivePlace(country);
                }}
                onmouseout={() => {
                  setActivePlace(null);
                }}
                icon={myicon}
              />
            );
          })}

          {activePlace && (
            <Popup
              position={[
                activePlace.coordinates.latitude,
                activePlace.coordinates.longitude
              ]}
            >
              <div>
                <h4>Country: {activePlace.country}</h4>
              </div>
            </Popup>
          )}
        </Map>
    );
  } else {
    return <div>Nothing</div>;
  }
};

export default MyMap;

m00
  • 297
  • 1
  • 5
  • 13
  • Your state is changing in the `` because you are calling the `setActivePlace` hook in `onmouseover` and `onmouseout` (which should be onMouseOver, onMouseOut, not sure why that's even working...) – djs Mar 28 '20 at 04:28
  • @DanielSchroederDev but i'm changing the activeState and not the myState, can you tell me a way to fix it? – m00 Mar 28 '20 at 04:47
  • You are changing the activePlace state, but it is set to a country, which is also in myState so it's updating both. – djs Mar 28 '20 at 04:51
  • For anyone with similar problem, see this [How to stop react re-rendering component, if part of the state changes?](https://stackoverflow.com/questions/60909344/how-to-stop-react-re-rendering-component-if-part-of-the-state-changes) – m00 Mar 29 '20 at 04:24

1 Answers1

1

Do you even need myState to be state? Can it just be a regular variable? That would solve your issue right away. If not, make a copy of myState, and then run map() with that. You will also need a flag to determine whether or not to run the map function, so setup a runMap state hook:

const MyMap = () => {

  // *** Create a FLAG for the map() call
  const [runMap, setRunMap] = useState(false);
  const [activePlace, setActivePlace] = useState(null);

  // setup the data to be function-scoped
  let data;

  const getData = async () => {
    let response = await axios
      .get('https://corona.lmao.ninja/v2/jhucsse')
      .catch(err => console.log(err));

    // just use the data here
    data = response.data;

    // set your runMap flag
    setRunMap(true);
  }

// later on...

if (runMap) {

// ...

  {data.map(country => {

As a side-note, you aren't supposed to be calling hooks this way, inside of nested functions and all. They are supposed to be called at the top-level of the component. There are a bunch of rules honestly, in your case it seems like using a class-based component and replacing your useEffect hook with componentDidMount might be safer, with the downside of being more verbose.

djs
  • 3,947
  • 3
  • 14
  • 28
  • I did that but it's all the same, i don't know why – m00 Mar 28 '20 at 04:56
  • Infact now the state is constantly re-rendering on mouse over and won't stop, fml – m00 Mar 28 '20 at 04:58
  • yeah i tried that already but it gets stuck on
    Nothing
    ; response that i sent in else statement or the markers won't load if i map them outside of return of MyMap :/
    – m00 Mar 28 '20 at 05:16
  • OK, try that updated solution then. It looks like you were using myMap just as a flag in that case, so just create another state variable to be the flag. – djs Mar 28 '20 at 05:22
  • Still, the markers won't show up, i initialized `let data = [];`, set `data = response.data` and flag to true, but inside `if(runMap)` i logged value of data in the console and it was empty for some reason. – m00 Mar 28 '20 at 05:32
  • Yeah, I mean that could be because you are using hooks wrong. Have you tried the class-based approach? – djs Mar 28 '20 at 05:36
  • Hooks can be kind of weird, and if you are running them anywhere but the top-level of the function component, and especially inside of a nested function that is async, the state may not be getting updated when you think. – djs Mar 28 '20 at 05:37
  • I see, thanks for helping. I might go for class based component but first i think i'll take another look at useMemo and try to figure it out that way. – m00 Mar 28 '20 at 05:41
  • 1
    Gotcha. I would also rethink your conditional rendering. You generally don't see code like you have there. Maybe find a better way besides wrapping the whole return in an if. Good luck! – djs Mar 28 '20 at 05:44