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

Overlay hydration mismatch #62

Open
Rafcin opened this issue Aug 1, 2022 · 0 comments
Open

Overlay hydration mismatch #62

Rafcin opened this issue Aug 1, 2022 · 0 comments

Comments

@Rafcin
Copy link

Rafcin commented Aug 1, 2022

Solution added below, opened for tracking and discussion.

Describe the bug A clear and concise description of what the bug is.
In short, overlay throws a hydration mismatch because of createPortal

To Reproduce Steps to reproduce the behavior:

  1. Spin up a Nextjs app,
  2. Add the package
  3. Use the sample with the overlay component
  4. That's it.

Desktop (please complete the following information):

  • OS: Ubuntu 20
  • Browser Chrome
  • Version 102.0.5005.115

Additional context Add any other context about the problem here.

The solution I put in the OverlayView was to add a mounted state and either create the portal or return null. I don't know if this is the acceptable solution for this project, but it works pretty well.

import React, { useContext, useEffect, useState } from "react"
import ReactDOM from "react-dom"
import { NYC_LATLNG } from "../../constants"
import { GoogleMapContext } from "../../context/"
import { OverlayViewProps } from "../../types"

export const OverlayView = ({
  pane = "overlayMouseTarget",
  position = NYC_LATLNG,
  children,
  onClick,
  onDoubleClick,
  onMouseDown,
  onMouseOut,
  onMouseOver,
  onMouseUp,
  onTouchEnd,
  onTouchStart,
  disableMapHits = false,
  disableMapHitsAndGestures = false,
}: OverlayViewProps): React.ReactPortal | null => {
  if (typeof document === "undefined") return null

  const { state } = useContext(GoogleMapContext)
  const [container] = useState<HTMLDivElement>(document.createElement("div"))
  const [overlay, setOverlay] = useState<google.maps.OverlayView | undefined>(
    undefined,
  )
  const [mounted, setMounted] = useState<boolean>(false)

  useEffect(() => {
    setMounted(true)
  }, [])

  useEffect(() => {
    if (state.map === undefined) return
    const overlay = new google.maps.OverlayView()
    overlay.onAdd = () => {
      container.style.position = "absolute"
      container.onclick = onClick || null
      container.ondblclick = onDoubleClick || null
      container.onmousedown = onMouseDown || null
      container.onmouseover = onMouseOver || null
      container.onmouseout = onMouseOut || null
      container.onmouseup = onMouseUp || null
      container.ontouchend = onTouchEnd || null
      container.ontouchstart = onTouchStart || null

      // @types/googlemap does not define `preventMapHitsFrom` or `preventMapHitsAndGesturesFrom`
      if (disableMapHitsAndGestures)
        (google.maps.OverlayView as any).preventMapHitsAndGesturesFrom(
          container,
        )
      else if (disableMapHits)
        (google.maps.OverlayView as any).preventMapHitsFrom(container)

        // Use an ugly cast to avoid package bundle issue
      ;(overlay.getPanes() as any)[pane].appendChild(container)
    }
    overlay.onRemove = () => {
      container.parentNode && container.parentNode.removeChild(container)
    }
    overlay.setMap(state.map)
    setOverlay(overlay)
    return () => overlay.setMap(null)
  }, [state.map])

  useEffect(() => {
    if (overlay !== undefined) {
      overlay.setMap(null)
      overlay.draw = () => {
        const location = overlay
          .getProjection()
          .fromLatLngToDivPixel(
            new google.maps.LatLng(position.lat, position.lng),
          )
        container.style.left = JSON.stringify(location?.x && location.x) + "px"
        container.style.top = JSON.stringify(location?.y && location.y) + "px"
      }
      overlay.setMap(state.map as google.maps.Map)
    }
  }, [overlay, position])

  return mounted ? ReactDOM.createPortal(children, container) : null
}

OverlayView.displayName = "OverlayView"

Btw I think this library is greater than all the other Google map libraries. It's elegant, it's clean, and it's well typed. I love it!

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

1 participant