Prop did not match. Client: X, Server: Y #14469
-
Bug reportDescribe the bugWhen I use JS to detect screen width, and then render content based on that screen width, the initial render differs on the server vs. client. The result is an error saying prop When this happens, my app loads up with the style value from the server, and doesn't refresh using the client side's styles. To ReproduceMinimal reproducible example: https://github.com/nandorojo/next-style-issue
// import { useDimensions } from "@react-native-community/hooks";
export default function App() {
let breakpoint = 0
// 👇 This generates a fake device width.
const width = Math.round(Math.random() * 1000)
// in my real app, I use a dependency:
// const { width = 0 } = useDimensions().window;
if (width > 500) breakpoint = 1
if (width > 900) breakpoint = 2
/*
* on server: { breakpoint: 0 }
* on client: { breakpoint: 0, 1, 2 } (unknown until it loads)
*/
console.log('[pages/index.tsx]', { breakpoint })
/* 🚨 Prop `style` did not match.
* Server: "background-color:green;min-height:100px"
* Client: "background-color:blue;min-height:200px
*/
return (
<div
style={{
backgroundColor: ['green', 'blue', 'orange'][breakpoint],
minHeight: ['100px', '200px', '300px'][breakpoint]
}}
>
{breakpoint} {/* <- this prints the correct breakpoint... */}
</div>
)
} Expected behaviorMy style is generated based on the screen width. Since the style on the client is different from the style generated on the server, I expect the style to rehydrate when the page loads. However, the style only updates when I resize the window + update component state. If I don't resize and update the component state, it stays the same. ScreenshotsSystem information
Additional contextAdd any other context about the problem here. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 7 replies
-
This is to be expected as the server doesn't have a Use next/dynamic with components/ResizeWindow/index.js import { useDimensions } from "@react-native-community/hooks";
function ResizeWindow() {
let breakpoint = 0;
const { width = 0 } = useDimensions().window;
if (width > 500) breakpoint = 1;
if (width > 900) breakpoint = 2;
return (
<div
style={{
backgroundColor: ["green", "blue", "orange"][breakpoint],
minHeight: ["100px", "200px", "300px"][breakpoint],
}}
>
{breakpoint}
</div>
);
}
export default ResizeWindow; pages/index.js (import the import dynamic from "next/dynamic";
const ResizeWindow = dynamic(() => import("../components/ResizeWindow"), { ssr: false });
const App = () => <ResizeWindow/>
export default App; OR Use something similar to Material-UI's NoSsr component... components/NoSSR/index.js import { useEffect, useLayoutEffect, useState } from "react";
import PropTypes from "prop-types";
const useEnhancedEffect =
typeof window !== "undefined" && process.env.NODE_ENV !== "test"
? useLayoutEffect
: useEffect;
const NoSSR = ({ children, defer, fallback }) => {
const [isMounted, setMountedState] = useState(false);
useEnhancedEffect(() => {
if (!defer) setMountedState(true);
}, [defer]);
useEffect(() => {
if (defer) setMountedState(true);
}, [defer]);
return isMounted ? children : fallback;
};
NoSSR.propTypes = {
children: PropTypes.node.isRequired,
defer: PropTypes.bool,
fallback: PropTypes.node,
};
NoSSR.defaultProps = {
defer: false,
fallback: null,
};
export default NoSSR; pages/index.js (import the import ResizeWindow from "../components/ResizeWindow";
import NoSSR from "../components/NoSSR";
const App = () => (
<NoSSR>
<ResizeWindow/>
</NoSSR>
);
export default App; Both options provide an optional |
Beta Was this translation helpful? Give feedback.
-
Hmm, the only problem is, I want the components to render on the server for SEO. Why can’t the props on the client and server be different, and it just rehydrates on the client side? |
Beta Was this translation helpful? Give feedback.
-
As mentioned above, the server doesn't have a |
Beta Was this translation helpful? Give feedback.
-
I fixed it by removing cors extension from chrome browser, i feel silly now not testing localhost on another browser before adding all the instructions up here as it didn't work |
Beta Was this translation helpful? Give feedback.
As mentioned above, the server doesn't have a
window
(it's undefined); therefore, the hook will always fall back to0
. As such, the only time thestyle
prop will match is if the device window is initially less than 500px.