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

NextJS SSR: Prop type do not match when mapping components from array #2134

Closed
AlexanderArvidsson opened this issue Nov 24, 2020 · 8 comments · Fixed by #2534
Closed

NextJS SSR: Prop type do not match when mapping components from array #2134

AlexanderArvidsson opened this issue Nov 24, 2020 · 8 comments · Fixed by #2534
Labels

Comments

@AlexanderArvidsson
Copy link

AlexanderArvidsson commented Nov 24, 2020

Current behavior:

When using the css prop on elements that are mapped from an array, the following error shows up in Firefox but not in Chrome:

Warning: Prop `className` did not match. Server: "css-1443u2l" Client: "css-1inpyqe-Home"
div
withEmotionCache/<@http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606256478140:1044:73
div
withEmotionCache/<@http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606256478140:1044:73
Home
MyApp@http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606256478140:11083:1
ErrorBoundary@http://localhost:3000/_next/static/chunks/main.js?ts=1606256478140:781:47
ReactDevOverlay@http://localhost:3000/_next/static/chunks/main.js?ts=1606256478140:885:20
Container@http://localhost:3000/_next/static/chunks/main.js?ts=1606256478140:12985:20
AppContainer@http://localhost:3000/_next/static/chunks/main.js?ts=1606256478140:13474:18
Root@http://localhost:3000/_next/static/chunks/main.js?ts=1606256478140:13599:18

Here is the code that causes the problems:

export default function Home() {
  return (
    <div css={{ background: "red" }}>
      {[1, 2, 3].map((_, i) => (
        <div key={i} css={{ background: "red" }} />
      ))}
    </div>
  );
}

To reproduce:

I cannot reproduce this in a CodeSandbox since this is dependent on NextJS SSR capabilities.
However, I have set up a clean repository to reproduce the issue, with only the required dependencies.
https://github.com/AlexanderArvidsson/emotion-bug-ssr

Please take a look at the top of the component in pages/index.tsx.
The repository is not using TypeScript, but I am having the exact same problem in my TypeScript project. Only difference is that it is using twin.macro, TailwindCSS and emotion. Should not matter though since I reproduced the issue without any of those packages.

Keep in mind that this issue only occurs in Firefox for me.
I've read around and found a couple of issues, some related to styled-components, some with ThemeUI.
The closes issue I found was #1462 (comment) and system-ui/theme-ui#538 (comment) but it did not help me resolve the issue.

Interestingly enough, it does not seem to happen after a production deployment to Vercel: https://bug-ssr.vercel.app/

Expected behavior:

The property should match after hydrating.

Environment information:

  • NextJS version: 10.0.3
  • react version: 17.0.1
  • @emotion/react version: 11.1.1
  • Firefox version: 83.0 (64-bit)
  • Windows 10
  • WSL 2, Ubuntu 18.04.4 LTS
@AlexanderArvidsson
Copy link
Author

I also tried this on my separate computer, this time a Macbook Pro.
Same issue using the repository I specified above, and as expected, issue only exists in Firefox and not Chrome.

@Andarist
Copy link
Member

Thank you for the report - this issue only affects a dev environment (so that's at least some upside of this). I'm looking into resolving it holistically.

@Andarist
Copy link
Member

I've prepared a PR that aims to fix this once and for all: #2136 🤞

@AlexanderArvidsson
Copy link
Author

I tested your branch in my project and it works in Firefox, but I have noticed another similar problem.
This time, this error only happens in Chrome.

Warning: Prop `className` did not match. Server: "css-6wu9uq" Client: "css-15w2jol-Button"
    at button
    at EmotionCssPropInternal (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606328819222:1525:73)
    at Button (http://localhost:3000/_next/static/chunks/pages/index.js?ts=1606328819222:690:23)
    at div
    at EmotionCssPropInternal (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606328819222:1525:73)
    at Index (http://localhost:3000/_next/static/chunks/pages/index.js?ts=1606328819222:102976:146)
    at div
    at MotionComponent (http://localhost:3000/_next/static/chunks/pages/index.js?ts=1606328819222:40344:80)
    at MountTransition (http://localhost:3000/_next/static/chunks/pages/index.js?ts=1606328819222:2123:23)
    at PresenceChild (http://localhost:3000/_next/static/chunks/pages/index.js?ts=1606328819222:42271:23)
    at AnimatePresence (http://localhost:3000/_next/static/chunks/pages/index.js?ts=1606328819222:42398:23)
    at main
    at EmotionCssPropInternal (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606328819222:1525:73)
    at div
    at EmotionCssPropInternal (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606328819222:1525:73)
    at Layout (http://localhost:3000/_next/static/chunks/pages/index.js?ts=1606328819222:2158:24)
    at FuegoProvider (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606328819222:54757:949)
    at ReduxFirestoreProvider (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606328819222:111230:1031)
    at ReactReduxFirebaseProvider (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606328819222:111204:1488)
    at App (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606328819222:125441:24)
    at Provider (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606328819222:111719:20)
    at withRedux(App) (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1606328819222:74372:35)
    at ErrorBoundary (http://localhost:3000/_next/static/chunks/main.js?ts=1606328819222:781:47)
    at ReactDevOverlay (http://localhost:3000/_next/static/chunks/main.js?ts=1606328819222:885:23)
    at Container (http://localhost:3000/_next/static/chunks/main.js?ts=1606328819222:12910:5)
    at AppContainer (http://localhost:3000/_next/static/chunks/main.js?ts=1606328819222:13399:24)
    at Root (http://localhost:3000/_next/static/chunks/main.js?ts=1606328819222:13524:24)

I have yet to identify exactly what is causing this issue. Once I figure it out, I will update the repository for you!

@AlexanderArvidsson
Copy link
Author

AlexanderArvidsson commented Nov 25, 2020

I have now updated the repo with the new issue.
It seems to be related to forwardRef + displayName, check the following code:

// It seems like having a forwardRef with displayName causes the mismatch.
const Button = React.forwardRef(({ children }, ref) => {
  return <button css={{ background: "red" }}>{children}</button>;
});

Button.displayName = "Button";

Removing either the displayName or forwardRef gets rid of the error.

According to https://reactjs.org/docs/forwarding-refs.html#displaying-a-custom-name-in-devtools, it seems you are supposed to pass a named function if you want the name to be correct.
However, the following code results in the exact same problem as described earlier:

const Button = React.forwardRef(function Button({ children }, ref) {
  return <button css={{ background: "red" }}>{children}</button>;
});

@AlexanderArvidsson
Copy link
Author

AlexanderArvidsson commented Nov 25, 2020

Changing the regex to this seems to have solved the issue for me:

      let match = error.stack.match(
        /(^.+)\n.*(renderWithHooks|finishClassComponent|processChild|ReactDOMServerRenderer.render)/m
      )

Tested in both Chrome and Firefox.
Now I do not know if this has any side-effects, please check it out yourself!

Extracted from this trace:
image


Update: Seems like it's not as simple as that. When using forwardRef without a named function or with displayName, the stack trace only shows Object.render rather than Object.Button. And in firefox's case, there isn't even any mention of the displayName at all in the browser.

The second example (see above) using named function works in both Chrome and Firefox.

I am unsure if setting displayName like I did first is even supported. It does work when looking in DevTools, but the docs suggest using a named function like in my second example. Do you think there is a way to support both anyway?

@Andarist
Copy link
Member

Thank you for double-checking my efforts! You have certainly uncovered some nice scenarios to test here:

  • forwardRef (I have totally forgotten about it)
  • ReactDOMServerRenderer.render - I guess you had to test a component pass directly as the root of SSR, a nice catch as well

.displayName can't be supported at all as it won't ever be put into stack traces, on any engine.

I will give this more thought to figure out the best solution that covers all those scenarios.

@florian-milky
Copy link

@Andarist I am running into the same issue with NextJS 13, React 18 and forwardRef in Chrome.
If I remove the array or the forwardRef it works fine

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

Successfully merging a pull request may close this issue.

3 participants