警告:没想到服务器 HTML 在 <li> <ul>React/Redux 服务器端渲染中包含 .



我是第一次使用 React 和 Redux 进行服务器端渲染,似乎遇到了一些困难。我收到警告:

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

我已经查过了,这意味着存在 html 树不匹配。我不确定这是怎么回事。有没有明显的解决方法?这是我拥有的引发警告的代码。

import React, { Component } from 'react';
import { connect } from 'react-redux';
import actions from '../actions';
class UsersList extends Component {
  componentDidMount() {
    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);

这是客户端.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')
);

问题是,由于服务器已经呈现了用户列表,因此它有一个ul,其中包含li。但是当客户端加载时,没有初始数据,因此只有一个ul,没有伴随它的li

要解决这个问题,我认为没有多少人会这样做,因为无论如何都需要在服务器端渲染中执行此操作,方法是将一些初始状态传递到 createStore redux 函数中:

您需要在两个位置执行此操作。在服务器端的 html 和客户端的入口点中。

简短的例子可以是:

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

如您所见,我们必须将初始状态设置为您在服务器端创建的存储。

在您的客户端:

// 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')
);

现在你可以访问这个全局变量window.INITIAL_STATE,你只需将其传递到createStore的第二个参数,即初始状态。

我现在

在这里发帖,因为我正在使用nextJS SSG搜索这个确切的问题。就像戴夫·牛顿(Dave Newton)提到的,在jsx返回中,如果由于对<li>的条件检查而为空,我将简单地添加一个检查,并且不会渲染<ul>。像这样:

           {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>
              : ''
            }