Предупреждение: React попытался повторно использовать разметку в контейнере, но контрольная сумма оказалась неверной.

Я пытаюсь заставить работать изоморфное приложение 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,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);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);
});

И мои маршруты.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>
);

person HelpMeStackOverflowMyOnlyHope    schedule 04.11.2015    source источник


Ответы (7)


Примечание. Это относится к более старым версиям React. Если вы используете React 16, вам следует использовать ReactDOM.hydrate()

Кроме того, приведенное ниже предложение приведет к повторному рендерингу на стороне клиента, как это предлагается в одном из ответов ниже.


Это может показаться безумно простым, но в своем серверном шаблоне оберните разметку React дополнительным <div>:

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

Почему это работает? На клиенте React имеет склонность оборачивать рендеринг вашего корневого компонента лишним div. ReactDOMServer.render, похоже, не ведет себя таким образом, поэтому при изоморфном рендеринге в один и тот же контейнер контрольная сумма Adler-32 вашего DOM отличается.

person James Wright    schedule 04.11.2015
comment
Я не понимаю, что такое шаблон на стороне сервера в этом случае. Я включил свой код рендеринга на стороне клиента и сервера выше в вопрос, а также файл маршрутов. - person HelpMeStackOverflowMyOnlyHope; 04.11.2015
comment
Ааа, вы используете модуль Iso и возвращаете статический HTML. Извините, но я не смогу вам конкретно помочь, так как у меня мало опыта в этой настройке, но мой ответ сработал для меня во многих проектах при использовании механизма шаблонов Express и передаче моего рендеринга React на стороне сервера как просмотреть недвижимость. - person James Wright; 04.11.2015
comment
Вау, этот ответ нашел способ найти! Это работает и для меня! - person vutran; 04.03.2016
comment
После удачного поиска в Google это, вероятно, сэкономило мне часы возни. Спасибо +1 - person IndelibleHeff; 17.03.2016
comment
Спасибо @JamesWright. Я визуализирую свой корневой компонент, используя react-dom/renderToString, и вставляю его в документ, используя dangerouslySetInnerHTML={{ __html: content }}, но это решение все еще работает, когда я заворачиваю content в пустой элемент div. - person Crisu83; 13.05.2016
comment
Спасибо, я ударился головой, потому что я рендерил внутри div, который раньше содержал другой контент. - person Seder; 15.08.2016
comment
Большое спасибо @JamesWright! Команда Facebook - есть ли для этого открытая проблема на github? - person tome; 05.09.2016
comment
Красиво, получилось. - person Dan Ochiana; 13.11.2016
comment
Это сработало изумительно и для меня. Убедительное объяснение. - person Sudhir; 24.11.2016
comment
Это работает, но в то же время кажется безумным. Разве это не должно быть задокументировано/исправлено где-то? - person Jesse; 25.11.2016
comment
Это не решение. Это удаляет поверхностный рендеринг на стороне клиента и вместо этого выполняет гораздо более медленный рендеринг, полностью удаляя существующий DOM и заменяя его новым результатом. Как следствие, он также проглатывает эту ошибку и любые другие ошибки, которые могут у вас возникнуть. - person Alex Faunt; 28.11.2016
comment
@Alex'Shop'Faunt Вызовет ли это указание родительского элемента вокруг рендеринга React на стороне сервера? Конечно, этот подход позволит избежать повторного рендеринга, поскольку теперь существует паритет между деревом DOM клиента и сервера. Однако, если это не так, не могли бы вы дать ссылку на источник, чтобы подтвердить это? Я до сих пор не могу найти никаких других решений этой проблемы через год после того, как опубликовал этот ответ. Спасибо! - person James Wright; 28.11.2016

Для тех, кто гуглит и приходит сюда, один странный способ решить эту проблему — это когда вы даже не используете изоморфный рендеринг (то есть ничего не рендерите на стороне сервера). Это произошло со мной при использовании шаблона с HtmlWebpackPlugin для обработки файла index.html.

В моем файле index.html я уже сам включил файл bundle.js, а вышеуказанный плагин также включает еще один файл bundle.js через script-src. Убедитесь, что вы устанавливаете inject: false в свой конструктор HtmlWebpackPlugin.

person ragebiswas    schedule 12.02.2016
comment
Точно! Я использую Rails, и мой связанный js-файл загружался дважды. Однажды, когда я включил его в свой шаблон страницы, а также с require_tree . в application.js. Я удалил строку require_tree . в application.js, но это может быть не лучшим решением для всех. - person Justin; 14.11.2016

Для меня полностью убил nodejs и перезапустил

person Sajjad Ashraf    schedule 12.05.2016
comment
у меня тоже сработало, но мне все еще интересно, в чем причина и как предотвратить эту проблему - person TMG; 16.08.2016
comment
я тоже, это звучит странно, но ЭТО РАБОТАЕТ - person Dicky Tsang; 08.11.2016
comment
Проблема может заключаться в том, что у вас нет горячей перезагрузки на сервере, но есть на клиенте. Таким образом, когда вы вносите изменения, код клиента обновляется, а код сервера — нет, и они дают разные результаты. Перезапуск гарантирует, что клиент и сервер используют один и тот же код (пока он снова не изменится). - person lowellk; 21.02.2018

ВНИМАНИЕ Популярный ответ здесь неверен. Что он делает, так это полностью удаляет существующий DOM и заменяет его новым рендерингом на клиенте. Это означает, что вы теряете быстрый поверхностный рендеринг из React и теряете производительность, и, как следствие, он также проглатывает ошибку OP и любые другие ошибки, которые могут у вас возникнуть.

Похоже, ваша проблема связана с CSS - если вы используете автопрефиксер и встроенные стили, это объясняет вашу разницу здесь.

Серверная сторона отобразила align-items:center, и клиент понял, что это в браузере webkit, и автоматически добавил для вас префикс -webkit-align-items.

Пожалуйста, опубликуйте дополнительную информацию о настройке CSS и любом компоненте, использующем встроенные стили или аналогичные.

person Alex Faunt    schedule 28.11.2016
comment
У вас есть источник, с помощью которого вы можете подтвердить это, например. файл в исходном коде React? Я не могу найти ничего, чтобы предложить это, плюс я ожидаю, что предупреждение о повторном использовании разметки будет выдано, даже если первоначальный рендеринг будет потерян. В настоящее время я не уверен, что он проглатывает ошибку. - person James Wright; 29.11.2016
comment
Извините - давно не заходил. изображение: imgur.com/a/bbOFr код: github.com/facebook/react/blob/ разметки в дополнительном div вы обнаружите, что «shouldReuseMarkup» является ложным. - person Alex Faunt; 06.04.2017
comment
Ого, ну вы конечно глубоко вникли! Спасибо за указание на это. Я не буду удалять свой ответ ради потомков, но обязательно укажу эту информацию в качестве предупреждения. - person James Wright; 06.04.2017

Если вы визуализируете свой основной контент внутри компонента макета, вам нужно будет визуализировать макет как статическую разметку (без атрибутов реакции), чтобы контрольная сумма контента совпадала между клиентом и сервером.

Сервер:

app.get('/', (req, res) => {
  // render the content to a string so it has the same checksum as on the client
  // render the layout to static markup so that it does affect the checksum
  // this ensures that rendering is isomorphic and client doesn't override server markup
  const content = reactDomServer.renderToString(<MyContent />)
  const html = '<!DOCTYPE html>' + reactDomServer.renderToStaticMarkup(<HtmlLayout content={content} />)
  res.send(html)
})

Макет HTML:

export default class HtmlLayout extends React.Component<any, any> {
  public render () {
    return (
      <html lang='en'>
      <head>
        <meta charSet='utf-8' />
        <meta name='viewport' content='width=device-width, initial-scale=1' />
        <title>Untitled</title>
        <link rel='stylesheet' href='/style/bundle.css' />
      </head>
      <body>
        { /* insert the content as a string so that it can be rendered separate with its own checksum for proper server-side rendering */ }
        <div id='content' dangerouslySetInnerHTML={ {__html: this.props.content} } />
        <script src='scripts/bundle.js'></script>
      </body>
      </html>
    )
  }
}

Клиент:

const root = document.getElementById('content')    
DOM.render(<MyContent />, root)

Ссылка: http://jeffhandley.github.io/QuickReactions/20-final-cleanup

person Raine Revere    schedule 03.06.2016

В моем случае проблема была вызвана тем фактом, что я использовал компонент MediaQuery из «реагирующего» без передачи свойства «значение», которое используется компонентом, когда он не может получить доступ к ширине экрана (например, на сервере ).

person matteo    schedule 28.10.2016

Я столкнулся с этой проблемой в приложении Isomorphic, над которым работал. Что сработало для меня, хотите верьте, хотите нет, это очистка кеша и жесткая перезагрузка приложения в Chrome. Похоже, что старый DOM каким-то образом кэшировался в браузере :)

person nikjohn    schedule 20.07.2017
comment
Не работает для меня. Кто-нибудь действительно понимает эту ошибку? - person Trevor Hickey; 15.11.2018