Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

avoid re-rendering map when rendering children #240

Closed
simoneb opened this issue Apr 8, 2016 · 6 comments
Closed

avoid re-rendering map when rendering children #240

simoneb opened this issue Apr 8, 2016 · 6 comments

Comments

@simoneb
Copy link

simoneb commented Apr 8, 2016

This is probably more a general question about react but I'd really appreciate any input about how to handle this scenario.

I have a map and some markers with possibly info windows on them. I need to control the info windows so that clicking anywhere on the map closes all the open info windows.

I'm keeping an array of items in the state of the component that wraps the map and they represent the data to display in the info windows (each entry in the array corresponds to an open info window).

The problem I'm having is that every time an interaction with the map triggers the opening or the closing on an info window (thus mutating the state kept in the map-wrapping component), the entire map re-renders thus, for instance, resetting the zoom level and the center position on the map, which may have changed because the user may have zoomed or panned the map.

What I'd like to undertand is, am I doing something obviously wrong or as soon as I put some data in the state then I should put everything in the state (in this case, zoom level and center whenever they change) so that a re-rendering of the map keep the last zoom level and position without resetting them to their default values?

@tomchentw
Copy link
Owner

We're going to look into this shortly.

We're also looking for maintainers. Involve in #266 to help strengthen our community!

@alaingilbert
Copy link

@simoneb were you able to find a solution ?
I have the same problem.

@Maushundb
Copy link

+1 here as well

@Maushundb
Copy link

Maushundb commented Dec 11, 2017

Ok folks, have a workaround here for anyone in need, and happens to be using Redux. Ideally one would submit a PR to actually fix this (+1 for maintainers), but a bandaid for now:

The issue here is that the marker / infowindow / whatever child component needs to rerender without triggering a render on the parent map component. In my case, I want to change the icon of a marker anytime a user hovers over a search result, and the reverse as well, but you can do it with whatever child component you want. What we can do is introduce a connected component between the map component and the child component to trigger a rerender only in the child while manually writing the rendering logic for our outer map component like so:

const MarkerClustererWithMarkers = props => {
  return (
    <MarkerClusterer
      averageCenter
      enableRetinaIcons
      gridSize={GRID_SIZE}
      minimumClusterSize={MIN_CLUSTER_SIZE}
    >
      {props.markers.map(marker => (
        <Marker
          icon={
            marker.index === props.activeSearchResultIndex
              ? markerImgSelected
              : markerImg
          }
          key={marker.index}
          position={{lat: marker.lat, lng: marker.lng}}
          onMouseOver={() => onActiveSearchResultChange(marker.index)}
          onMouseOut={() => onActiveSearchResultChange(-1)}
        />
      ))}
    </MarkerClusterer>
  );
};

const mapMarkerStateToProps = state => ({
  activeSearchResultIndex: activeSearchResultSelector(state),
});

const mapMarkerDispatchToProps = (dispatch: Dispatch<Action>) => {
  return {
    onActiveSearchResultChange: index => dispatch(activateSearchResult(index)),
  };
};

// Create a connected HOC with the marker cluster / whatever child you want. This will forceUpdates to it whenever the desired state changes, even if the parent doesn't update
const MarkerClustererWithMarkersConnected = connect(
  mapMarkerStateToProps,
  mapMarkerDispatchToProps,
)(MarkerClustererWithMarkers);

// Define our map component
class SearchMap extends React.Component<Props> {
  constructor() {
    super();
    this._loadingElement = <div>LOADING</div>;
    this._containerElement = <div className={styles.root} />;
    this._mapElement = <div style={{height: `100%`}} />;
  }

  shouldComponentUpdate() {
  // force our map to never update after the initial load. If there is a case where you do want it to update, put that logic here
    return false;
  }

  render(): React.Node {
    const SearchGoogleMap = withScriptjs(
      withGoogleMap(props => (
        <GoogleMap
          ref={props.onMapLoad}
          defaultZoom={DEFAULT_ZOOM}
          defaultCenter={SF_LAT_LONG}
          onClick={props.onMapClick}
        >
          <MarkerClustererWithMarkersConnected markers={this.props.markers} /> // render our HOC
        </GoogleMap>
      )),
    );

    return (
      <SearchGoogleMap
        googleMapURL={MAP_URL}
        loadingElement={this._loadingElement}
        containerElement={this._containerElement}
        mapElement={this._mapElement}
        markers={this.props.markers}
      />
    );
  }
}
const mapStateToProps = state => ({
  markers: markerDataSelector(state),
});

export default connect(mapStateToProps)(SearchMap);

Ideally you'd break this out into two separate files, but you get the general idea – lock the parent map component and use redux connected components to forceUpdates on the child component exclusively.

@CodingToBeWithHer
Copy link

for the specific case of center you can use defaultCenter instead of just center - it'll not recenter on infowindow click. Similarly should work for zoom

@CodingToBeWithHer
Copy link

but I agree problem is I need sometimes recenter the map so I can't use defaultCenter so it is indeed a problem in some cases

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants