react-google-maps icon indicating copy to clipboard operation
react-google-maps copied to clipboard

[Bug] Warning: useLayoutEffect does nothing on the server

Open adrenaline681 opened this issue 1 year ago • 10 comments

Description

After installing the react-google-maps package we started getting the following error:

Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://reactjs.org/link/uselayouteffect-ssr for common fixes.
2024-05-24T18:49:02.004220106Z     at Map (file:///app/node_modules/@vis.gl/react-google-maps/dist/index.modern.mjs:805:5)

In our own project we used something like this to prevent this warning issue. const useIsomorphicLayoutEffect = typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect

This means that it will use useLayoutEffect normally, but during Server Side Rendering it will use useEffect

Steps to Reproduce

Use react-google-maps inside of a NextJS project.

Environment

  • Library version: 0.11.0
  • Google maps version: weekly
  • Browser and Version: Chrome
  • OS: Alpine Linux

Logs

Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://reactjs.org/link/uselayouteffect-ssr for common fixes.
2024-05-24T18:49:02.004220106Z     at Map (file:///app/node_modules/@vis.gl/react-google-maps/dist/index.modern.mjs:805:5)

adrenaline681 avatar May 24 '24 18:05 adrenaline681

We certainly don't want to trigger these warnings when rendering on the server, but I'm trying to figure out what would be the best way to do that.

The suggested solution (useIsomorphicLayoutEffect()) doesn't feel like a good choice – it's just pretending not to use useLayoutEffect when in fact it will use it when it counts. This can lead to problems (not in our case since we're not modifying the DOM, more in general).

In case of our Map component which is triggering the warning, I have two thoughts:

  • First, it really doesn't make much sense to have that rendered on the server at all, since all that's ever going to come from it is an empty div. So maybe we should just add a 'use client'; directive to the map and related components to make it clear that it will never render on the server? Or does it make more sense to leave that to the user and add some documentation that you should always wrap the Map etc into a client-only component when using SSR?

  • The other point is, we're using the layout-effects to make sure that changes to the camera-related props are forwarded to the Maps API as quickly as possible. The question here is, do we actually need the effects for this? Maybe we can be even faster with that by passing the values to the google.maps.Map instance directly from the render-function? Are there examples for something like that similar in other react libraries?

usefulthink avatar May 26 '24 20:05 usefulthink

Having the same issue

For a temporary fix where do I put that code?

const useIsomorphicLayoutEffect = typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect All the warnings make it difficult to read the console.logs when debugging

I am using Remix so I guess it's rendering something server side

Error message:

 Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the 
initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://reactjs.org/link/uselayouteffect-ssr 
for common fixes.
10:58:14 │ remix      │     at Map (file:///home/kjavitz/Learning/routing/node_modules/@vis.gl/react-google-maps/dist/index.modern.mjs:805:5)
10:58:14 │ remix      │     at MapWrapper (/home/kjavitz/Learning/routing/app/components/mapwrapper.jsx:18:69)
10:58:14 │ remix      │     at APIProvider (file:///home/kjavitz/Learning/routing/node_modules/@vis.gl/react-google-maps/dist/index.modern.mjs:286:7)

kevinjavitz avatar May 31 '24 05:05 kevinjavitz

I've seen this recommended several times, mostly related to unit-testing, but you could give this a shot: Somewhere in the code that exclusively runs on the server (no Idea how that works with remix, sorry), you could add this code that disables the useLayoutEffect hook entirely:

import React from 'react';

React.useLayoutEffect = React.useEffect;

Let me know if that works.

usefulthink avatar May 31 '24 05:05 usefulthink

For Remix, I don't have much experience with Remix either, but maybe you can use the client module setting to avoid server rendering if you haven't tried it yet.

Server vs. Client Code Execution | Remix - https://remix.run/docs/en/main/discussion/server-vs-client#splitting-up-client-and-server-code

shuuji3 avatar May 31 '24 05:05 shuuji3

For Remix, I don't have much experience with Remix either, but maybe you can use the client module setting to avoid server rendering if you haven't tried it yet.

Server vs. Client Code Execution | Remix - https://remix.run/docs/en/main/discussion/server-vs-client#splitting-up-client-and-server-code

For Remix you could also just implement a useEffect to make sure it doesn't hit the server. Something like:

const [ shouldRender, setShouldRender ] = useState(false);

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

return (
    shouldRender ? (
        <Map... >
            ...
        </Map>
    ) : (
        <>Loading Map...</>
    )
)

pgmccullough avatar Sep 10 '24 20:09 pgmccullough

Any updates on how to fix this?

asivaneswaran avatar Oct 07 '24 19:10 asivaneswaran

Any updates?

RicardoAALL avatar Oct 28 '24 19:10 RicardoAALL

For now the recommendation is just "avoid rendering components that don't do anything on the server as server-components" (we will probably come up with something better at some point). If anyone here has experience with browser-only components in next.js and remix, let me know how we could improve this.

usefulthink avatar Oct 29 '24 08:10 usefulthink

For remix.run you can use the remix-utils

import { ClientOnly } from "remix-utils/client-only"

<ClientOnly fallback={<FallbackComponent />}>
				{() => <MapComponent />}
			</ClientOnly>

braginteractive avatar Nov 27 '24 20:11 braginteractive

to resolve this issue in nextjs pages router.

create a similar file to this, make sure that the default export is the Map component

import { Map, useMap, APIProvider } from "@vis.gl/react-google-maps"
import type { MapProps } from "@vis.gl/react-google-maps"

export { useMap, APIProvider }
export type { MapProps }
export default Map

import the Map component with next/dynamic like so. ( make sure to pass ssr: false ).

import dynamic from "next/dynamic"
// you can import other export normally without next dynamic.
import { useMap } from "@/components/map-component"

const MapComponent = dynamic(() => import("@/components/map-component"), { ssr: false })

a-eid avatar Feb 21 '25 03:02 a-eid