0

I am working on a react app with react leaflet. When you click on a country on the leaflet map, the country is supposed to change style (turn a different colour). I can't get this to work. In the current implementation the mouseover, and mouseout events work, but not click. At the moment, the click changes the style for a split second during the click event, then returns to the default style. I need the color change to be persist beyond the click event

If anyone has any idea how to fix this, I would be very grateful:

App.jsx

function App() {
  const {
    fetchCountryData,
    fetchPeopleIndicatorData,
    fetchEconomicIndicatorData,
    fetchEnvironmentIndicatorData,
  } = useCountryData();

  const [clickedCountry, setClickedCountry] = useState(null);
  const [countryData, setCountryData] = useState([]);
  const [peopleIndicatorData, setPeopleIndicatorData] = useState([]);
  const [environmentIndicatorData, setEnvironmentIndicatorData] = useState([]);
  const [economicIndicatorData, setEconomicIndicatorData] = useState([]);
  const [sidebarIsOpen, setSidebarIsOpen] = useState(false);

  const handleCountryClick = async (country) => {
    if (clickedCountry === country) {
      setClickedCountry(null);
      setSidebarIsOpen(false);
    } else {
      setClickedCountry(country);
      const countryData = await fetchCountryData(country.properties.ISO_A3);
      setCountryData(countryData);
      setPeopleIndicatorData(
        await fetchPeopleIndicatorData(country.properties.ISO_A3)
      );
      setEconomicIndicatorData(
        await fetchEconomicIndicatorData(country.properties.ISO_A3)
      );
      setEnvironmentIndicatorData(
        await fetchEnvironmentIndicatorData(country.properties.ISO_A3)
      );
      setSidebarIsOpen(true);
    }
  };

  const defaultStyle = {
    fillColor: "#000",
    fillOpacity: 0.7,
    weight: 3,
    opacity: 0.7,
    dashArray: 3,
    color: "black",
  };

  const highlightStyle = {
    fillOpacity: 0.7,
    weight: 5,
    dashArray: "",
    color: "#807c7c",
    fillColor: "#ffffff",
  };

  const clickStyle = {
    fillColor: "#fa0000",
    fillOpacity: 1,
    weight: 5,
    opacity: 1,
    dashArray: "",
    color: "#807c7c",
  };

  const onEachCountry = (country, layer) => {
    layer.on({
      click: (e) => {
        e.target.setStyle(clickStyle);
        handleCountryClick(country);
      },
      mouseover: (e) => {
        if (clickedCountry !== country) {
          e.target.setStyle(highlightStyle);
        }
      },
      mouseout: (e) => {
        if (clickedCountry !== country) {
          e.target.setStyle(defaultStyle);
        }
      },
    });
  };

  const handleSidebarClose = () => {
    setClickedCountry(null);
    setSidebarIsOpen(false);
  };

  return (
    <div className="App">
      <div className="map-container">
        <MapContainer>
          <TileLayer
            url="https://api.maptiler.com/maps/basic-v2/{z}/{x}/{y}.png?"
          />
          <GeoJSON
            data={countriesData.features}
            style={defaultStyle}
            onEachFeature={onEachCountry}
          />
        </MapContainer>
      </div>
    </div>
  );
}

export default App;

1 Answer 1

0

Your on-click code is calling e.target.setStyle(....) which sets the style of the element being targeted, and then calls handleCountryClick() which sets different state variable values.

The setting of different state variable values will cause the component to re-render, which I suspect is losing the value applied to the e.target.setStyle(.....).

I think you need to set the "selected style" to a state variable, and then use that state variable in your code:

const [theGeoStyle, setTheGeoStyle] = useState(defaultStyle);

// ....

<GeoJSON
  data={countriesData.features}
  style={theGeoStyle}
  onEachFeature={onEachCountry}
/>

and change your on-click handler to use setTheGeoStyle(clickStyle)

Not the answer you're looking for? Browse other questions tagged or ask your own question.