0

I have an airbnb-like app with cards on the left and a google map on the right (using the react-google-maps package)

I would like to highlight (in the code below through an animation) a marker when the user hovers on its corresponding card.

I actually managed to do so (see code below) but the problem is, the map component rerenders when another card is hovered on by the user.

Is there a way to do so without having the map to rerender everytime ?

My App.js (simplyfied for comprehension purposes):

import React from "react";

import { Meals } from "../api/meals.js";
import { Restaurants } from "../api/restaurants.js";

import MealCard from "./MealCard";
import MealsMap from "./MealsMap";

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      highlightedMarker: ""
    };
    this.renderMeals = this.renderMeals.bind(this);
    this.highlightMarker = this.highlightMarker.bind(this);
  }

  renderMeals() {
    return this.props.meals.map(m => (
      <div
        className="col-sm-6 col-xs-12 "
        key={m._id}
        onMouseOver={() => this.highlightMarker(m.restaurant)}
      >
        <MealCard
          name={m.name}
          restaurant={
            this.props.restaurants.find(r => r._id === m.restaurant).name
          }
          image={m.image}
          address={
            this.props.restaurants.find(r => r._id === m.restaurant).address
          }
        />
      </div>
    ));
  }

  renderMap() {
    return (
      <MealsMap
        restaurants={this.props.restaurants}
        highlightedMarker={this.state.highlightedMarker}
      />
    );
  }

  highlightMarker(restaurantId) {
    this.setState({ highlightedMarker: restaurantId });
  }

  render() {
    return (
      <div>
        <div className="app-wrapper" style={{ display: "flex" }}>
          <div className="container">
            <div className="row">{this.renderMeals()}</div>
          </div>
          {this.renderMap()}
        </div>
      </div>
    );
  }
}

and my MealsMap.js:

import React from "react";
import { withGoogleMap, GoogleMap, Marker } from "react-google-maps";

class MealsMap extends React.Component {
  render() {
    const GoogleMapMeals = withGoogleMap(props => (
      <GoogleMap
        defaultCenter={{ lat: 50.6320134, lng: 3.0568584 }}
        defaultZoom={13}
      >
        {this.props.restaurants.map(r => (
          <Marker
            key={r._id}
            position={{ lat: Number(r.lat), lng: Number(r.lng) }}
            animation={
              this.props.highlightedMarker === r._id
                ? google.maps.Animation.BOUNCE
                : ""
            }
          />
        ))}
      </GoogleMap>
    ));
    return (
      <GoogleMapMeals
        containerElement={
          <div
            style={{
              flex: "0 0 400px",
              height: "100vh",
              position: "sticky",
              top: "0"
            }}
          />
        }
        mapElement={
          <div
            style={{
              height: "100%",
              width: "100%",
              position: "absolute",
              top: "0px",
              left: "0px",
              backgroundColor: "rgb(229, 227, 223)"
            }}
          />
        }
      />
    );
  }
}
export default MealsMap;
Tholle
  • 108,070
  • 19
  • 198
  • 189
Uj Corb
  • 1,959
  • 4
  • 29
  • 56

1 Answers1

2

You don't want to define the GoogleMapMeals component inside of the render method of MealsMap, since that will result in a new component each render which will make React unmount the previous one and create an entirely new one.

You could define GoogleMapMeals outside of the render method instead.

Example

const GoogleMapMeals = withGoogleMap(props => (
  <GoogleMap
    defaultCenter={{ lat: 50.6320134, lng: 3.0568584 }}
    defaultZoom={13}
  >
    {props.markers.map(r => (
      <Marker
        key={r._id}
        position={{ lat: Number(r.lat), lng: Number(r.lng) }}
        animation={
          props.highlightedMarker === r._id
            ? google.maps.Animation.BOUNCE
            : ""
        }
      />
    ))}
  </GoogleMap>
));

class MealsMap extends React.Component {
  render() {
    return (
      <GoogleMapMeals
        markers={this.props.restaurants}
        highlightedMarker={this.props.highlightedMarker}
        containerElement={
          <div
            style={{
              flex: "0 0 400px",
              height: "100vh",
              position: "sticky",
              top: "0"
            }}
          />
        }
        mapElement={
          <div
            style={{
              height: "100%",
              width: "100%",
              position: "absolute",
              top: "0px",
              left: "0px",
              backgroundColor: "rgb(229, 227, 223)"
            }}
          />
        }
      />
    );
  }
}
Tholle
  • 108,070
  • 19
  • 198
  • 189
  • 1
    Hi @Tholle, thank you very much for your help. I'm trying to use your solution but I can't access `this.props.highlightedMarker` (11th line of your code sample in your answer); It gives me undefined. – Uj Corb Aug 10 '18 at 12:53
  • 1
    @JulesCorb Ah, sorry about that. I updated the answer. – Tholle Aug 10 '18 at 12:54
  • Ok thank you ! it does work now but does not solve my problem. When I hover a card, it should trigger the Bounce animation on the corresponding marker, but it actually doesn't. – Uj Corb Aug 10 '18 at 12:55
  • @JulesCorb Did the hover animation work before? I might have misunderstood your question, but I thought the issue was that the entire map was recreated every render. – Tholle Aug 10 '18 at 12:57
  • 1
    sorry if I was not clear ! Yes the hover animation worked before; but indeed the problem was that the map re-renders every time another card is hovered on. For example, if my mouse is on card A, the corresponding marker bounces on the map, and then when I move my mouse over card B, the map component re renders entirely and the corresponding marker starts to bounce. I'm looking for a solution for the corresponding marker to bounce but without the map to rerender – Uj Corb Aug 10 '18 at 13:00
  • @JulesCorb That's alright. Very interesting. It might be that destroying the map and creating an entirely new one solved the marker animation issue if the animation can't be changed after the initial render. Maybe `key={r._id + '?highlighted=' + (props.highlightedMarker === r._id)}` on the `Marker` will work. – Tholle Aug 10 '18 at 13:09