Material-ui: Warning: Prop `className` did not match. Server: "MuiAvatar-img-154" Client: "MuiAvatar-img-148"

Created on 10 Apr 2018  Â·  16Comments  Â·  Source: mui-org/material-ui

When I use SSR to render my page, I found that only would have this warning. Other Components are working perfectly. More source code can be found in my repository cnode-proxy.
Please help me.

import React from 'react';
import PropTypes from 'prop-types';
import ListItem from 'material-ui/List/ListItem';
import ListItemAvatar from 'material-ui/List/ListItemAvatar';
import ListItemText from 'material-ui/List/ListItemText';
import Avatar from 'material-ui/Avatar';
import { withStyles } from 'material-ui/styles';
import { topicPrimaryStyle, topicSecondaryStyle } from './styles';
import { tabs } from '../../util/variable-define';
import cx from 'classnames';
import dateFormat from 'dateformat';

const Primary = ({ classes, topic }) => {
  const classNames = cx({
    [classes.tab]: true,
    [classes.top]: topic.top,
  });
  return (
    <span className={classes.root}>
      <span className={classNames}>{topic.top ? 'Top' : tabs[topic.tab]}</span>
      <span className={classes.title}>{topic.title}</span>
    </span>
  );
};

Primary.propTypes = {
  classes: PropTypes.object.isRequired,
  topic: PropTypes.object.isRequired,
};

const StyledPrimary = withStyles(topicPrimaryStyle)(Primary);

const Secondary = ({ classes, topic }) => (
  <span className={classes.root}>
    <span className={classes.userName}>{topic.author.loginname}</span>
    <span className={classes.count}>
      <span className={classes.replyCount}>Reply: {topic.reply_count}</span>
      <span>Visiting: {topic.visit_count}</span>
    </span>
    <span>Date: {dateFormat(topic.create_at, 'yy-mm-dd')}</span>
  </span>
);

Secondary.propTypes = {
  classes: PropTypes.object.isRequired,
  topic: PropTypes.object.isRequired,
};

const StyledSecondary = withStyles(topicSecondaryStyle)(Secondary);

const TopicListItem = ({ onClick, topic }) => (
  <ListItem button onClick={onClick}>
    <ListItemAvatar>
//Error would happend here
      <Avatar src={topic.author.avatar_url} />
    </ListItemAvatar>
    <ListItemText
      primary={<StyledPrimary topic={topic} />}
      secondary={<StyledSecondary topic={topic} />}
    />
  </ListItem>
);

TopicListItem.propTypes = {
  onClick: PropTypes.func.isRequired,
  topic: PropTypes.object.isRequired,
};

export default TopicListItem;

import React from 'react';
import { observer, inject } from 'mobx-react';
import PropTypes from 'prop-types';
import Helmet from 'react-helmet';
import Container from '../layout/container';
import Tabs, { Tab } from 'material-ui/Tabs';
import TopicListItem from './list-item';
import List from 'material-ui/List';
import { CircularProgress } from 'material-ui/Progress';
import queryString from 'query-string';
import { tabs } from '../../util/variable-define';

@inject((stores) => {
  return {
    appState: stores.appState,
    topicStore: stores.topicStore,
  };
})
@observer
export default class TopicList extends React.Component {
  static contextTypes = {
    router: PropTypes.object,
  };
  constructor() {
    super();
    this.onTabChange = this.onTabChange.bind(this);
    this.onListItemClick = this.onListItemClick.bind(this);
  }
  componentDidMount() {
    const tab = this.getTab();
    this.props.topicStore.fetchTopics(tab);
  }
  componentWillReceiveProps(nextProps) {
    if (nextProps.location.search !== this.props.location.search) {
      this.props.topicStore.fetchTopics(this.getTab(nextProps.location.search));
    }
  }
  onTabChange(e, value) {
    this.context.router.history.push({
      pathname: '/list',
      search: `?tab=${value}`,
    });
  }
  onListItemClick(topic) {
    this.context.router.history.push(`/detail/${topic.id}`);
  }
  getTab(search) {
    const searchString = search || this.props.location.search;
    const query = queryString.parse(searchString);
    return query.tab || 'all';
  }
  bootstrap() {
    const query = queryString.parse(this.props.location.search);
    const tab = query.tab || 'all';
    const result = this.props.topicStore.fetchTopics(tab).then(() => {
      return true;
    }).catch(() => {
      return false;
    });
    return result;
  }
  render() {
    const {
      topicStore,
    } = this.props;
    const { syncing } = topicStore;
    const topicList = topicStore.topics;
    const { createdTopic } = topicStore;
    const { user } = this.props.appState;
    const tab = this.getTab();
    return (
      <Container>
        <Helmet>
          <title>Topic List</title>
        </Helmet>
        <div>
          <Tabs value={tab} onChange={this.onTabChange}>
            {
              Object.keys(tabs).map(tabKey => (
                <Tab key={tabKey} label={tabs[tabKey]} value={tabKey} />
              ))
            }
          </Tabs>
        </div>
        {
          createdTopic && createdTopic.length > 0 ?
            <List>
              {
                createdTopic.map((topic) => {
                  topic = Object.assign({}, topic, {
                    author: user.info,
                  });
                  return (
                    <TopicListItem key={topic.id} onClick={() => { this.onListItemClick(topic); }} topic={topic} />
                  );
                })
              }
            </List> :
            null
        }
        <List>
          {
            topicList.map(topic => (
              <TopicListItem
                key={topic.id}
                onClick={() => { this.onListItemClick(topic); }}
                topic={topic}
              />
            ))
          }
        </List>
        {
          syncing ? (
            <div style={{ display: 'flex', justifyContent: 'space-around', padding: '40px 0' }}>
              <CircularProgress color="secondary" size={100} />
            </div>
          ) : null
        }
      </Container>
    );
  }
}
TopicList.wrappedComponent.propTypes = {
  appState: PropTypes.object.isRequired,
  topicStore: PropTypes.object.isRequired,
};
TopicList.propTypes = {
  location: PropTypes.object.isRequired,
};

Most helpful comment

@vpaul08, I finally found the solution for my problem. But it was closely related to the combination of nextjs and material-ui. Would it help you if I document it here?

All 16 comments

@gzcisco720: How did you solve your issue?

@riddla Yes, I have solved it.

@gzcisco720: But _how_ did you do it? Can you elaborate on the steps you needed to take?

@riddla Yes, of course, my problem caused by the difference between versions. The element structure has been changed. The was needed outside in before, but, in new version, you only need to simply use .

@riddla and also, Sorry for replying you late, hopefully your issues has been fixed already.

I am also having a similar issue. I had a look at your code, and assume that this commit was your solution:

https://github.com/gzcisco720/cnode-proxy/commit/c9f577f16463ec789b82af0fe86fe6aacae1261a

I am not sure this is a sufficient solution though.

I figured out my issue. I had multiple export default withRoot(withStyles(classes)(MyComponent)).

withRoot should only be used once the top level page component.

@jakerobers I recently have helped some others fix this issue, I found that similar Error would also be caused by using the wrong way to implement SSR in server.js.

I'm still struggling with this. I don't know how you guys managed to get it working with SSR. Pardon me for saying that I couldn't understand the English used by some people who have solved this problem. Any proper understandable translation of the resolution may help. Thanks in advance.

@vpaul08, I finally found the solution for my problem. But it was closely related to the combination of nextjs and material-ui. Would it help you if I document it here?

@riddla yes please. However I've implemented the feature without mui. I used foundation components instead but knowing what caused this issue may help in future.

@riddla Did you document your solution somewhere? I have the same problem.

@bbigras, @vpaul08, sorry that it took so long for me to respond.

In my case I did strictly follow the structure of the official example over at https://github.com/mui-org/material-ui/tree/master/examples/nextjs to solve the problem:

I had previously tried to wrap the <Component/> within https://github.com/mui-org/material-ui/blob/master/examples/nextjs/pages/_app.js with recurring components like <Grid/> and <Typography/> – which ended up in the Prop className did not match error message.

I then used a wrapping component directly in the different pages.

@riddla thanks! Did you manage to make the error report thing work?

this thing:
image

with https://github.com/mui-org/material-ui/tree/master/examples/nextjs I only have an internal error when an error happens.

@bbigras, sorry I did not encounter that error ...

Hi, our project is the same, and the issues is the same too, reference your solution,I did solve the problem,but I don't know why.I reed the doc and the official demos,the official use this structure

<ListItemAvatar>
  <Avatar>
      <FolderIcon />
   </Avatar>
</ListItemAvatar>

so I don't know why we can't use this structure, why this structure will make ssr console error?
Looking forward to your reply, thanks a lot.

Was this page helpful?
0 / 5 - 0 ratings