admin管理员组

文章数量:1252688

I am doing Server Side Rendering for the first time with React and Redux and seeming to be having a little difficulty. I am getting the warning:

Warning: Did not expect server HTML to contain a <li> in <ul>.

I have looked this up and it means that there is a html tree mismatch. I am not sure how that is. Is there an obvious way to fix it? Here is the code I have that is throwing the warning.

import React, { Component } from 'react';
import { connect } from 'react-redux';
import actions from '../actions';

class UsersList extends Component {
  ponentDidMount() {
    if (this.props.users.length > 0) {
      return;
    }
    this.props.fetchUsers();
  }

  render() {
    const { users } = this.props;
    return (
      <div>
        <ul>
          {users.map(user => {
            return <li key={user.id}>{user.name}</li>;
          })}
        </ul>
      </div>
    );
  }
}

const stateToProps = state => {
  return {
    users: state.users
  };
};

const dispatchToProps = dispatch => {
  return {
    fetchUsers: () => dispatch(actions.fetchUsers())
  };
};

const loadData = store => {
  return store.dispatch(actions.fetchUsers());
};

export { loadData };
export default connect(stateToProps, dispatchToProps)(UsersList);

Here is the client.js:

// Startup point for the client side application
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';

import Routes from './Routes';
import reducers from './reducers';

const store = createStore(reducers, {}, applyMiddleware(thunk));

ReactDOM.hydrate(
  <Provider store={store}>
    <BrowserRouter>
      <div>{renderRoutes(Routes)}</div>
    </BrowserRouter>
  </Provider>,
  document.querySelector('#root')
);

I am doing Server Side Rendering for the first time with React and Redux and seeming to be having a little difficulty. I am getting the warning:

Warning: Did not expect server HTML to contain a <li> in <ul>.

I have looked this up and it means that there is a html tree mismatch. I am not sure how that is. Is there an obvious way to fix it? Here is the code I have that is throwing the warning.

import React, { Component } from 'react';
import { connect } from 'react-redux';
import actions from '../actions';

class UsersList extends Component {
  ponentDidMount() {
    if (this.props.users.length > 0) {
      return;
    }
    this.props.fetchUsers();
  }

  render() {
    const { users } = this.props;
    return (
      <div>
        <ul>
          {users.map(user => {
            return <li key={user.id}>{user.name}</li>;
          })}
        </ul>
      </div>
    );
  }
}

const stateToProps = state => {
  return {
    users: state.users
  };
};

const dispatchToProps = dispatch => {
  return {
    fetchUsers: () => dispatch(actions.fetchUsers())
  };
};

const loadData = store => {
  return store.dispatch(actions.fetchUsers());
};

export { loadData };
export default connect(stateToProps, dispatchToProps)(UsersList);

Here is the client.js:

// Startup point for the client side application
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';

import Routes from './Routes';
import reducers from './reducers';

const store = createStore(reducers, {}, applyMiddleware(thunk));

ReactDOM.hydrate(
  <Provider store={store}>
    <BrowserRouter>
      <div>{renderRoutes(Routes)}</div>
    </BrowserRouter>
  </Provider>,
  document.querySelector('#root')
);
Share Improve this question asked Oct 30, 2017 at 14:13 Taylor AustinTaylor Austin 6,00716 gold badges63 silver badges108 bronze badges 2
  • According to stackoverflow./a/45371427/1427878 this sems to happen when “you are somehow initially rendering a different tree on the client vs the server” – C3roe Commented Oct 30, 2017 at 14:16
  • 2 Yes but how do I fix it ? – Taylor Austin Commented Oct 30, 2017 at 14:17
Add a ment  | 

2 Answers 2

Reset to default 8

The problem was that since the server had rendered the list of users already, it had a ul with lis. But when the client loaded there was no initial data so there was only an ul and no lis to acpany it.

To fix this problem, which I think not many will have because you need to do this anyway in server side rendering is to pass some initial state into the createStore redux function:

You will need to do this in two places. In the html on your server side and in your entry point on the client side.

Short example could be:

<html>
  <head></head>
  <body>
    <div id="root">${content}</div>
    <script>
      window.INITIAL_STATE = ${serialize(store.getState())}
    </script>
    <script src="bundle.js"></script>
  </body>
</html>

As you can see we have to set the initial state to the store that you created on your server side.

On your client side:

// Startup point for the client side application
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';

import Routes from './Routes';
import reducers from './reducers';

const store = createStore(reducers, window.INITIAL_STATE, applyMiddleware(thunk));

ReactDOM.hydrate(
  <Provider store={store}>
    <BrowserRouter>
      <div>{renderRoutes(Routes)}</div>
    </BrowserRouter>
  </Provider>,
  document.querySelector('#root')
);

Now you have access to this global variable window.INITIAL_STATE and you just pass that through into the second argument of createStore which is the initial state.

I'm posting here now because I was searching this exact issue with nextJS SSG. Just like Dave Newton mentioned, in the jsx return I would simply add a check and not render the <ul> if it is empty due to a conditional check on the <li>'s. Something like this:

           {Object.keys(el[i]).length > 0 ? 
              <ul>
                {getKeys(docs, item, el).map(
                  ([key, value], i) => {
                    const name = `${docs[item][el][key].name}`
                    const path = `${docs[item][el][key].path}`
                    return (name !== "undefined") && (
                    <li key={i}><a href={path}>{name}</a></li>
                  )}
                )}
              </ul>
              : ''
            } 

本文标签: