node.js - 警告 : React attempted to reuse markup in a container but the checksum was invalid

标签 node.js express reactjs webpack isomorphic-javascript

我正在尝试让同构的 Node.js、Express、Webpack、React 应用程序正常工作。我收到以下错误。关于如何修复它的任何建议?

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
 (client) rgin:0;display:flex;-webkit-align-items:
 (server) rgin:0;display:flex;align-items:center;j

warning @   warning.js:45
ReactMount._mountImageIntoNode  @   ReactMount.js:807
wrapper @   ReactPerf.js:66
mountComponentIntoNode  @   ReactMount.js:268
Mixin.perform   @   Transaction.js:136
batchedMountComponentIntoNode   @   ReactMount.js:282
Mixin.perform   @   Transaction.js:136
ReactDefaultBatchingStrategy.batchedUpdates @   ReactDefaultBatchingStrategy.js:62
batchedUpdates  @   ReactUpdates.js:94
ReactMount._renderNewRootComponent  @   ReactMount.js:476
wrapper @   ReactPerf.js:66
ReactMount._renderSubtreeIntoContainer  @   ReactMount.js:550
ReactMount.render   @   ReactMount.js:570
wrapper @   ReactPerf.js:66
(anonymous function)    @   client.jsx:14
(anonymous function)    @   iso.js:120
each    @   iso.js:21
bootstrap   @   iso.js:111
(anonymous function)    @   client.jsx:12
__webpack_require__ @   bootstrap d56606d95d659f2e05dc:19
(anonymous function)    @   bootstrap d56606d95d659f2e05dc:39
(anonymous function)    @   bootstrap d56606d95d659f2e05dc:39

这是服务器最初传递给浏览器的内容:

<!doctype html>
<html lang="">

    <head>
        <title>my title</title>

        <meta name="apple-mobile-web-app-title" content="my title" data-react-helmet="true" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" data-react-helmet="true" />
<meta name="apple-mobile-web-app-capable" content="yes" data-react-helmet="true" />
<meta name="mobile-web-app-capable" content="yes" data-react-helmet="true" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" data-react-helmet="true" />
<meta name="description" content="my description." data-react-helmet="true" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" data-react-helmet="true" />
<meta charset="utf-8" data-react-helmet="true" />

        <link rel="stylesheet" href="/assets/styles/reset.css" data-react-helmet="true" />
<link rel="stylesheet" href="/assets/styles/base.css" data-react-helmet="true" />
<link rel="stylesheet" href="/assets/styles/Carousel.css" data-react-helmet="true" />
<link rel="stylesheet" href="/assets/styles/main.css" data-react-helmet="true" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Condensed" type="text/css" data-react-helmet="true" />
<link rel="icon" href="/assets/185bb6f691241307862b331970a6bff1.ico" type="image/x-icon" data-react-helmet="true" />

        SCRIPT

    </head>
    <body>
        <script src="https://cdn.firebase.com/js/client/2.2.7/firebase.js"></script>
        <script src="https://cdn.firebase.com/libs/reactfire/0.4.0/reactfire.min.js"></script>

        <div class="app">
<div class="___iso-html___" data-key="_0"><div data-reactid=".1hkqsbm9n9c" data-react-checksum="794698749"><div data-reactid=".1hkqsbm9n9c.0"><div data-reactid=".1hkqsbm9n9c.0.$=10"></div><div style="position:fixed;z-index:2;top:0;left:0;right:0;height:60px;color:rgb(219,219,219);font-family:mainnextcondensed_ultralight;font-size:17px;overflow:hidden;" data-reactid=".1hkqsbm9n9c.0.$/=11"><div style="position:absolute;left:0;top:0;background-color:rgba(27,27,27,0.92);padding-right:35px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10"><div style="float:left;height:60px;width:13px;border-left:5px solid rgb(210,45,164);" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10.$/=10"></div><div style="float:left;height:60px;width:227px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOMAAAAhCAYAAAArrhzzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACxNJREFUeNrsXF9sHEcZHx927MZJvU5cSw4FrxXahja451Q88ADeq5Aq0aKeKyh9oMqdyFNA9PzAY3U2vPDE3SGBxNOdaZHKC3c8FCgqvXP73PjqFiSSIl9oSSTXJOe4juv8M/Otv/F9O57ZP+dzaWF+ysjZ3dmd2dn5ze/7vpm5LtYGFr/0otV7wsrdXr1x8dbl6zV+qj7+9nebUZ6xtbXFDAwMWoiFJZ90qjjw7eOp0fJj2XtfeLR6d9Je4nls05wGBu2jO4CENlfABZ7YInsxwU+B+mV77x9Ixg73uHl67xtgR39w0rpWaQAZG6ZJDQw6TEZQw77xo+WRn33Fih3qYQfG7l6Av/2TIwz+euSVHx/+5miOE3aam6s106wGBtHR5UPG4vDzj6QOP/750A9be/mf7Mqv/pa/tbwxG+RDGp/RwCAEGTkRU+AXgin6uV8/yrpHDoZ+4J0Pb7LlH79Z/+idK43Dj486zRfOpzkxK4aMBgYRyciJGOd/qjy5QZtjv/wqu+vUUNsF/PsXf21yQiY4IeuGjAYGIX1GjJoWBRFBEVVE3Di3wtZfv8RunF9loJ79Xzum9CUBR7//kHV7eaPM/cmJqNMfXV1dofJhveN46JlmwcHFfR/jzxp8asjIkSOdWklE8AuXf/Km59z6/GUWy/ewI2e+yAa+c3zXPUM/ethee+U9MH3z+/QeZZ4c/D9EfWtIRAdVPtBHNjD4xJAR/cQUvdgz0u/JfOvy9V1E3PEV126yldwi2zzfZMPPP+K59uGr/wKlquzHC/B6zxAiympZNp+4s+Duhc3/2MIK4dZL09Sxg2REUy5HFXGQq1zQXUr8r8NuXx8bwdIpVC10ySPX/szJBm0w4yijsJqAYXOk/rBNyuBegtV7DB2BtEQ3yawvWMYZYSHzomToGowX+iLA1us7+RwK0iTPsX6vzHs8S/XX7+8c3zo6/daHVSrJHZegYoUhLIU59vFpGh4JA6YKrOYmholzRIigulaArOUJ+goY1ivmuiM2Elr+GEthbln0+8TBUiOKWyTXJuDHsOBSIc0aXsZBTKg0PcS/aESQMbnUCzgOZaswPicDJq5eU0b7AcRGekDRfm7tYOYymcEvPfsa2wlv8i8pus9JGLD/63f8EZNv3eKsYO3SZT1Uss5HTkI0dYsLyujWHge1TylpgGQ4y3JhGBEyfYKYbLFedmuucpJNYOpoTEpBaZkSwCOeYLzQFKqNHNY96SiMwrrhe2BkHUV2UMEQuKoik2f5wtfOI73yH5rXfFewnyf8ylfWCVzqDwNRYwgSdrv40YDCWn5mKvhySjPLVJlA99v80LLTzw4OUIcyBhb+8uSNxp0T7+rmDtZLnh9zMEzJ+yxV5/IwfarPdS5TOoq1EaFqQ6ZxCWigK7Jx9tsCdVZhVHRCTVk1XVoUc5pRWer+AUuQuL3xP/x+FqKlCImP5MGOx3eku6R1dGWfOIwpncW89QI4Ww0vZlUXlT1O616d78gi+a75YlV44QY4LTtHUObPK67efWlVgQVdmjQucf1Py2z1T/8fed4s3GVM7A1lbcpB3zQf+x76IjdpipmiF/rp3z5Tk3wI6HAtMwTUtpIyrjPrc02ie8IdcHOa+3zqK8KJjQ6XEaFDjMK2Nq6LMVic8tpkLcb9uJ6YcAdaxjf7H9fGFde9PfwNEu4l57+aLy/OCZE+7EPpitYukcTmG4W69k3Fi6FinCqJjGqKj2RpJRKSwSUpTUj7x5tntOEd4h4fcu/Foa69RQ+GFTeF1nGlakCGhY5MigUkZVL6DC6NqtqklxKdIrlofBih4XRHETKkXyGWj8TO9MiKCVeE6W1DFBVHwJ63cV+w8QdeJjWqOaD4jDaNu7S+r8RaZZcgQEA/LtBRenXpl98M9PzeAIEZj/7fHfWJL5HPlX6FBZBUFqGsKrTPSmvAcTg0gNaYuWdvuWXI//hS1cqC477/sJXSgeJ/GFT81C8S5FZ1vQZR6aHnd3ZcBCcCCnav+iLxmf+GPpweq30mHJaGDw/wSZjBkWcskUmKVHM+ORfgUAltldOvsGbDKuGzIaGEgBHIU/Ewowoc+JtWvJHAXMT9LdG/KKHAMDA40yRlVHAfo7OUDA7S1UH+wQ0Q3w3GfBTo40RjONmWpgEERGJOQMcIi19t/5AsgmNhOjKborGMK2N+OWiJNtWt/AIIiMEjGTzDtRrQWoI0xtSIsBgIjm1+EMDPZKxr2Yr6y1n0/1swSm9Q0MCGJhM+Lkt26+BsxPmLKASCn9uYNEB3f2GxgYZZRMVlj1ICZ9a2x7eVojasFGGQ0M9kBGQkrwHy3ZDzRkNDBoH/8RYAC6QbxY8FBYtQAAAABJRU5ErkJggg==);background-repeat:no-repeat;background-position:center;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10.$/=11"></div><div style="display:none;width:0;height:0;border-style:solid;border-width:6px 6px 0 6px;border-color:rgb(117,117,117) transparent transparent transparent;-webkit-transform:rotate(360deg);float:left;margin-left:6px;margin-top:26px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10.$/=12"></div></div><div style="position:absolute;top:0px;left:280px;width:340px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11"><div style="background-color:rgba(27,27,27,0.92);height:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=10"></div><div style="background-color:rgba(53,53,53,0.84);height:40px;position:relative;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11"><div style="position:absolute;top:0;bottom:0;left:0;right:0;padding:0;margin:0;display:flex;align-items:center;justify-content:center;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$=10"><div style="background-image:url(&#x27;/assets/3bec3e57cb5ee05658440d21984fb7b7.png&#x27;);background-repeat:no-repeat;background-position:-58px -194px;width:23px;height:22px;position:absolute;top:50%;left:10px;margin-top:-11px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$=10.$icon"></div></div><div style="position:absolute;left:40px;right:40px;top:0px;bottom:0px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$/=12"><input type="text" style="width:100%;height:100%;font-size:14px;font-family:mainnext_regular;background-color:transparent;color:#ffffff;" placeholder="SEARCH ARTISTS, TRACKS, ALBUMS" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$/=12.0"/></div></div><div style="background-color:rgba(27,27,27,0.92);height:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=12"></div></div><div style="position:absolute;top:0px;left:620px;right:0px;background-color:rgba(27,27,27,0.92);height:60px;line-height:60px;overflow:hidden;min-width:500px;padding-left:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12"><div style="position:absolute;top:0px;bottom:0px;right:0px;width:357px;padding-left:141px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0"><a class="" href="/import" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10"><div style="padding-left:40px;position:absolute;left:0px;top:10px;bottom:10px;cursor:pointer;line-height:40px;color:rgb(255,255,255);" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import"><div style="position:absolute;top:0;bottom:0;left:0;right:0;padding:0;margin:0;display:flex;align-items:center;justify-content:center;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import.$=10"><div style="background-image:url(&#x27;/assets/3bec3e57cb5ee05658440d21984fb7b7.png&#x27;);background-repeat:no-repeat;background-position:0px -194px;width:28px;height:28px;position:absolute;top:50%;left:0px;margin-top:-14px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import.$=10.$icon"></div></div><span data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import.1">Import Playlists</span></div></a><div style="margin-left:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$admin/=1$admin"><div style="cursor:pointer;float:left;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$admin/=1$admin.$login">Login</div></div></div></div></div></div><noscript data-reactid=".1hkqsbm9n9c.1"></noscript></div></div>
<div class="___iso-state___" data-key="_0" data-meta="{}" data-state="&quot;{\&quot;UserStore\&quot;:{\&quot;user\&quot;:{\&quot;authenticated\&quot;:false,\&quot;isWaiting\&quot;:false}},\&quot;SearchStore\&quot;:{\&quot;focused\&quot;:false,\&quot;input\&quot;:\&quot;\&quot;,\&quot;timeout\&quot;:null,\&quot;searchRequests\&quot;:[],\&quot;artists\&quot;:null,\&quot;artistsFailed\&quot;:false,\&quot;artistsLoading\&quot;:false,\&quot;tracks\&quot;:null,\&quot;tracksFailed\&quot;:false,\&quot;tracksLoading\&quot;:false,\&quot;albums\&quot;:null,\&quot;albumsFailed\&quot;:false,\&quot;albumsLoading\&quot;:false,\&quot;playlists\&quot;:null,\&quot;playlistsFailed\&quot;:false,\&quot;playlistsLoading\&quot;:false,\&quot;youtubes\&quot;:null,\&quot;youtubesFailed\&quot;:false,\&quot;youtubesLoading\&quot;:false,\&quot;soundclouds\&quot;:null,\&quot;soundcloudsFailed\&quot;:false,\&quot;soundcloudsLoading\&quot;:false},\&quot;PlayerStore\&quot;:{\&quot;player\&quot;:null,\&quot;playerSecond\&quot;:null,\&quot;playingTrack\&quot;:null,\&quot;playingTrackSecond\&quot;:null,\&quot;videoId\&quot;:null,\&quot;videoIdSecond\&quot;:null,\&quot;makingPlayingTrackPlayable\&quot;:false,\&quot;radio\&quot;:false,\&quot;startSeconds\&quot;:0,\&quot;current\&quot;:0,\&quot;total\&quot;:0,\&quot;perc\&quot;:0,\&quot;currentSecond\&quot;:0,\&quot;totalSecond\&quot;:0,\&quot;percSecond\&quot;:0,\&quot;playing\&quot;:false,\&quot;playingSecond\&quot;:false,\&quot;secondsListened\&quot;:0,\&quot;secondsListenedSecond\&quot;:0,\&quot;expand\&quot;:false,\&quot;source\&quot;:null,\&quot;tracksQueue\&quot;:[],\&quot;tracksPrevQueue\&quot;:[],\&quot;favorite\&quot;:false,\&quot;random\&quot;:false,\&quot;repeat\&quot;:false,\&quot;mute\&quot;:false,\&quot;volume\&quot;:100,\&quot;mode\&quot;:\&quot;standard\&quot;},\&quot;ImportStore\&quot;:{\&quot;url\&quot;:\&quot;\&quot;,\&quot;error\&quot;:false,\&quot;focused\&quot;:false,\&quot;loading\&quot;:false,\&quot;loaded\&quot;:false,\&quot;playlist\&quot;:null}}&quot;"></div>
</div>

        <!-- Google Analytics: change UA-XXXXX-X to be your site's ID -->
        <!--
        <script>
            (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
                (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
                    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
            })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
            ga('create', 'UA-XXXXX-X', 'auto');
            ga('send', 'pageview');
        </script>
        -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.3/fastclick.min.js"></script>
        <script type="text/javascript">
          if ('addEventListener' in document) {
            document.addEventListener('DOMContentLoaded', function() {
                FastClick.attach(document.body);
            }, false);
          }
        </script>
        <script type="text/javascript" charset="utf-8" src="/assets/app.js"></script>
    </body>
</html>

这是我的 server.jsx:

import Iso from 'iso';
import React from 'react';
import ReactDomServer from 'react-dom/server';
import { RoutingContext, match } from 'react-router'
import createLocation from 'history/lib/createLocation';

import alt from 'altInstance';
import routes from 'routes.jsx';
import html from 'base.html';

/*
 * @param {AltObject} an instance of the Alt object
 * @param {ReactObject} routes specified in react-router
 * @param {Object} Data to bootstrap our altStores with
 * @param {Object} req passed from Express/Koa server
 */
const renderToMarkup = (alt, state, req, res) => {
  let markup, content;
  let location = new createLocation(req.url);
  alt.bootstrap(state);

  match({ routes, location }, (error, redirectLocation, renderProps) => {
    if (redirectLocation)
      res.redirect(301, redirectLocation.pathname + redirectLocation.search)
    else if (error)
      res.status(500).send(error.message)
    else if (renderProps == null)
      res.status(404).send('Not found')
    else
      content = ReactDomServer.renderToString(<RoutingContext {...renderProps} />);
      markup = Iso.render(content, alt.flush());
  });

  return markup;
};

/* 
 * Export render function to be used in server/config/routes.js
 * We grab the state passed in from the server and the req object from Express/Koa
 * and pass it into the Router.run function.
 */
export default function render(state, req, res) {
  const markup = renderToMarkup(alt, state, req, res);
  return html.replace('CONTENT', markup);
};

这是我的 client.jsx:

import React from 'react';
import ReactDOM from 'react-dom';
import Iso from 'iso';
import createBrowserHistory from 'history/lib/createBrowserHistory';
import { Router } from 'react-router';
import alt from 'altInstance';
import routes from 'routes.jsx';

/*
 * Client side bootstrap with iso and alt
 */
Iso.bootstrap((state, _, container) => {
  alt.bootstrap(state);
  ReactDOM.render(<Router history={createBrowserHistory()} children={routes} />, container);
});

还有我的 routes.jsx:

import React from 'react';
import Route from 'react-router';
import App from 'components/App';
import ImportPlaylist from 'components/ImportPlaylist';
import Login from 'components/Login';
import Logout from 'components/Logout';
import Player from 'components/Player/Player';
import Test from 'components/Test';

export default (
  <Route path="/" component={App}>
    <Route path="login" component={Login} />
    <Route path="logout" component={Logout} />
    <Route name="test" path="test" component={Test} />        
    <Route name="import" path="import" component={ImportPlaylist} />
    <Route name="player" path="/:playlist" component={Player} />
  </Route>
);

最佳答案

注意:这适用于旧版本的 React。 如果你使用 React 16,你应该使用 ReactDOM.hydrate()

另外,以下建议将导致客户端重新渲染,如以下答案之一所建议的那样。


这听起来可能非常简单,但是在您的服务器端模板中,将您的 React 标记包装在一个额外的 <div> 中:

<!-- hypothetical handlebars template -->
<section role="main" class="react-container"><div>{{{reactMarkup}}}</div></section>

为什么会这样?在客户端,React 倾向于用多余的 div 包装它对根组件的渲染。 ReactDOMServer.render似乎没有以这种方式表现,因此当一个同构渲染到同一个容器中时,您的 DOM 的 Adler-32 校验和会有所不同。

关于node.js - 警告 : React attempted to reuse markup in a container but the checksum was invalid,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33521047/

相关文章:

node.js - 如何使用调试符号制作node-waf构建二进制文件?

javascript - 将 requirejs 与 Node 一起使用

javascript - express.js 中的这行代码是什么意思?

node.js - 迭代数组时超测双重回调

javascript - react : Programmatically Creating table <td>s

javascript - 如何在xampp的子目录中运行react应用程序?

Node.js API MongoDB 请求仅在本地主机上工作

sql - 从 SQL 注入(inject)生成的查询有多安全?

javascript - 如何将创建的联系人分配给 Mongoose 中的当前用户?

node.js - 当我没有 webpack.config.js 文件时在哪里添加disableHostCheck