Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Next-Expo app with Material-UI producing "Warning: Prop className did not match." #1605

Closed
5ervant opened this issue Feb 28, 2020 · 8 comments

Comments

@5ervant
Copy link

5ervant commented Feb 28, 2020

Using Material-UI or specifically implementing https://material-ui.com/components/app-bar/#back-to-top on a web project with @expo/next-adapter and refreshing the web browser resulting to have a console "Warning: Prop `className` did not match."

Environment

Expo CLI 3.13.1 environment info:
System:
OS: Windows 10
Binaries:
Yarn: 1.16.0 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
npm: 6.13.7 - C:\Program Files\nodejs\npm.CMD
Watchman: 4.9.4 - C:\Users\5ervant\AppData\Local\watchman\watchman.EXE
IDEs:
Android Studio: Version 3.5.0.0 AI-191.8026.42.35.6010548

  "dependencies": {
    "@expo/vector-icons": "~10.0.0",
    "@material-ui/core": "^4.9.4",
    "@material-ui/icons": "^4.9.1",
    "@react-native-community/masked-view": "0.1.5",
    "@react-navigation/bottom-tabs": "^5.0.0",
    "@react-navigation/native": "^5.0.0",
    "@react-navigation/stack": "^5.0.0",
    "@react-navigation/web": "~1.0.0-alpha.9",
    "expo": "~36.0.0",
    "expo-asset": "~8.0.0",
    "expo-constants": "~8.0.0",
    "expo-font": "~8.0.0",
    "expo-web-browser": "~8.0.0",
    "next": "^9.2.2",
    "react": "~16.9.0",
    "react-dom": "~16.9.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz",
    "react-native-gesture-handler": "~1.5.0",
    "react-native-safe-area-context": "0.6.0",
    "react-native-screens": "2.0.0-alpha.12",
    "react-native-web": "~0.11.7"
  }

Reproducible Demo

This is my wrapper <Layout> component implementing this Material-UI App Bar: https://material-ui.com/components/app-bar/#back-to-top

import PropTypes from 'prop-types';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import useScrollTrigger from '@material-ui/core/useScrollTrigger';
import Box from '@material-ui/core/Box';
import Container from '@material-ui/core/Container';
import Fab from '@material-ui/core/Fab';
import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp';
import Zoom from '@material-ui/core/Zoom';

const useStyles = makeStyles(theme => ({
  root: {
    position: 'fixed',
    bottom: theme.spacing(2),
    right: theme.spacing(2),
  },
}));

function ScrollTop(props) {
  const { children, window } = props;
  const classes = useStyles();
  // Note that you normally won't need to set the window ref as useScrollTrigger
  // will default to window.
  // This is only being set here because the demo is in an iframe.
  const trigger = useScrollTrigger({
    target: window ? window() : undefined,
    disableHysteresis: true,
    threshold: 100,
  });

  const handleClick = event => {
    const anchor = (event.target.ownerDocument || document).querySelector('#back-to-top-anchor');

    if (anchor) {
      anchor.scrollIntoView({ behavior: 'smooth', block: 'center' });
    }
  };

  return (
    <Zoom in={trigger}>
      <div onClick={handleClick} role="presentation" className={classes.root}>
        {children}
      </div>
    </Zoom>
  );
}

ScrollTop.propTypes = {
  children: PropTypes.element.isRequired,
  /**
   * Injected by the documentation to work in an iframe.
   * You won't need it on your project.
   */
  window: PropTypes.func,
};

export default function BackToTop(props) {
  return (
    <React.Fragment>
      <CssBaseline />
      <AppBar>
        <Toolbar>
          <Typography variant="h6">Scroll to see button</Typography>
        </Toolbar>
      </AppBar>
      <Toolbar id="back-to-top-anchor" />
      <Container>
        <Box my={2}>{props.children}</Box>
      </Container>
      <ScrollTop {...props}>
        <Fab color="secondary" size="small" aria-label="scroll back to top">
          <KeyboardArrowUpIcon />
        </Fab>
      </ScrollTop>
    </React.Fragment>
  );
}

I tried this solution vercel/next.js#7322 (comment) vercel/next.js#7423 (comment) by npm install --save-dev babel-plugin-styled-components but babel-plugin-styled-components takes forever to install.

Now I'm doing npm uninstall --save-dev babel-plugin-styled-components but uninstalling it also seems forever. I just cancel then run yarn to check dependencies.

My Observations

Having <Box my={2}>{props.children}</Box> is throwing a 'Warning: Prop `className` did not match. Server: "MuiBox-root MuiBox-root-136" Client: "MuiBox-root MuiBox-root-90"'

Having className={classes.root} is throwing a 'Warning: Prop `className` did not match. Server: "makeStyles-root-143" Client: "makeStyles-root-89"'

What do we need to do to get rid of those warnings?

Current Solution

Not to refresh your developing Next-Expo web page and just let it auto-update after editing your project source code.

@brentvatne
Copy link
Member

brentvatne commented Feb 28, 2020

hello! this is a next.js/styled-components issue as you pointed out. the solution does seem to be to install the styled-components babel plugin. I'm not sure why that would take forever to install, maybe an intermittent issue with your internet connection or the npm registry?

@5ervant
Copy link
Author

5ervant commented Mar 1, 2020

It's really very very slow, these are some of the installation loading processes that I'm seeing after npm install --save-dev babel-plugin-styled-components:

[..................] | fetchMetadata: sill removeObsoleteDep removing lodash@4.17.15 from the tree as its been replaced by a newer version or is 
[..................] | fetchMetadata: sill removeObsoleteDep removing esutils@2.0.3 from the tree as its been replaced by a newer version or is n 
[  ................] \ fetchMetadata: sill removeObsoleteDep removing react-timer-mixin@0.13.4 from the tree as its been replaced by a newer vers 
// etc...

@Axedyson
Copy link

Axedyson commented Mar 20, 2020

I had the same issue with the Box component className mismatches.

I fixed the issue by setting reactStrictMode inside my next.config.js file to false:

Udklip

I restarted my next js development server after the change in the file, and then it worked

@5ervant
Copy link
Author

5ervant commented Mar 20, 2020

@brentvatne I've successfully installed babel-plugin-styled-components as dependency, modified my "babel.config.js" with the following and restarted my server, but still the issue is still there. Mostly, the styles of my header or my site title is disappearing after a few refreshes.

module.exports = {
  presets: ['@expo/next-adapter/babel'],
  plugins: [
    ['styled-components', { ssr: true, displayName: true, preprocess: false }]
  ]
};

@AndysonDK My "next.config.js" structure is different because it's generated by next-expo.
Anyway, I tried the following and restarted but did still seeing "Warning: Prop `className` did not match. Server: "MuiTypography-root makeStyles-title-492 MuiTypography-h6" Client: "MuiTypography-root makeStyles-title-2 MuiTypography-h6"".

const { withExpo } = require('@expo/next-adapter');

module.exports = withExpo({
  projectRoot: __dirname,
  reactStrictMode: false
});

@Axedyson
Copy link

Axedyson commented Mar 20, 2020

Hmmmm, another solution might be to wrap all the components that throw warnings inside of a NoSsr component maybe:
https://material-ui.com/components/no-ssr/

This is probably not a good option for us people that want to server-side render everything though :(

@5ervant
Copy link
Author

5ervant commented Mar 25, 2020

@AndysonDK I don't know, but Material-UI seems rendering its styles on the client dynamically.
Yes, after a few milliseconds before the Material-UI's styling appears.

Did you know why the client-side styling is happening, I guess because of createMuiTheme or theme?
Or it's the nature of Material-UI?

@Axedyson
Copy link

Axedyson commented Mar 25, 2020

I'm not sure, but I've heard that because the Box component is dynamic it will do some client-side processing or something like that. But I haven't noticed any of my styles being rendered client-side though. I do use StyleProvider:

image

So my styles are in fact rendered client-side (injected at the top of the <head></head> tags) but they are also rendered server-side to increase SEO performance and to avoid lag of styling when a user requests a page.

I'm making sure to remove the server-side rendered CSS so there is no duplicate since it's also being rendered client-side:
image

I don't think createMuiTheme or theme is running on the client-side.
But yeah, I'm not 100% sure.

Here is the plan for the new material ui version if you haven't already seen it:
mui/material-ui#20012
They mention something with className mismatches.

@5ervant
Copy link
Author

5ervant commented Mar 25, 2020

MyDocument.getInitialProps solution: https://github.com/mui-org/material-ui/blob/master/examples/nextjs/pages/_document.js

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

No branches or pull requests

3 participants