PauCasademont
PauCasademont

Reputation: 287

React-Leaflet same marker in multiple Layers

How can I add the same marker in multiple Layers in react-leaflet?

For example:

I have an app to search for restaurants with react leaflet and every marker is a different restaurant. I want to have a LayerControl to filter them by type and rating (1..5 stars).

I can have a LayerControl.Overlay to filter by types (e.g. Buffet) but I also want to filter by rating. If I have a restaurant type='buffet' and rating=4 how can I do it for display the marker only when buffet type and 4 star rating are checked.

Here is my current code that filters only by type.

I'm using react-leaflet v3.

Upvotes: 0

Views: 1205

Answers (1)

Seth Lutske
Seth Lutske

Reputation: 10676

The method you are using with creating various layergroups to manage your filters is not the best way to go about it. I took another approach.

Create a state object with your desired filters

In your App, create a state variable that holds potential filters. We'll set it to initially contain all options:

const restaurantTypes = ["Family Style", "Buffet", "Fast Food", "Cafe"];
const ratings = [1, 2, 3, 4, 5];

function App() {
  const [filters, setFilters] = useState({
    restaurantTypes,
    ratings
  });
  ...
}

Custom filter component

I opted to create a custom component, which is basically the exact same html markup as a leaflet layercontrol, but without any of the functionality. You can check out the sandbox at the end of this answer, but all it does is manage the filters state variable we just defined. Here's a brief summary:

const FilterControl = ({ filters, setFilters }) => {
  return (
    <div>
      <h3>Type</h3>
      {restaurantTypes.map((type) => (
        <label>
          <input
            type="checkbox"
            checked={filters.restaurantTypes.includes(type)}
            onClick={(e) => {
              if (filters.restaurantTypes.includes(type)) {
                setFilters((prevFilters) => ({
                  ...prevFilters,
                  restaurantTypes: prevFilters.restaurantTypes.filter(
                    (f) => f !== type
                  )
                }));
              } else {
                setFilters((prevFilters) => ({
                  ...prevFilters,
                  restaurantTypes: [...prevFilters.restaurantTypes, type]
                }));
              }
            }}
          />
          <span>{type}/span>
        </label>
      ))}
      <h3>Rating</h3>
      {ratings.map((rating) => (
        // same as above but for ratings
      ))}
    </div>
  );
};

So now your FilterControl can neatly manage the state of filters.

Filter the markers according to filter

Now all you have to do is map over your restaurants dataset, and only render markers whose type and rating are in the current filters:

function App() {
  const [filters, setFilters] = useState({
    restaurantTypes,
    ratings
  });
  return (
    <MapContainer ... >
      <TileLayer ... />
      <FilterControl filters={filters} setFilters={setFilters} />
      {restaurants.map((restaurant) => {
        if (
          filters.restaurantTypes.includes(restaurant.type) &&
          filters.ratings.includes(restaurant.rating)
        ) {
          return <Marker position={restaurant.coordinates} />
        }
        return null;
      })}
    </MapContainer>
  );
}

Working codesandbox

Upvotes: 1

Related Questions