同形の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('/assets/3bec3e57cb5ee05658440d21984fb7b7.png');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('/assets/3bec3e57cb5ee05658440d21984fb7b7.png');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=""{\"UserStore\":{\"user\":{\"authenticated\":false,\"isWaiting\":false}},\"SearchStore\":{\"focused\":false,\"input\":\"\",\"timeout\":null,\"searchRequests\":[],\"artists\":null,\"artistsFailed\":false,\"artistsLoading\":false,\"tracks\":null,\"tracksFailed\":false,\"tracksLoading\":false,\"albums\":null,\"albumsFailed\":false,\"albumsLoading\":false,\"playlists\":null,\"playlistsFailed\":false,\"playlistsLoading\":false,\"youtubes\":null,\"youtubesFailed\":false,\"youtubesLoading\":false,\"soundclouds\":null,\"soundcloudsFailed\":false,\"soundcloudsLoading\":false},\"PlayerStore\":{\"player\":null,\"playerSecond\":null,\"playingTrack\":null,\"playingTrackSecond\":null,\"videoId\":null,\"videoIdSecond\":null,\"makingPlayingTrackPlayable\":false,\"radio\":false,\"startSeconds\":0,\"current\":0,\"total\":0,\"perc\":0,\"currentSecond\":0,\"totalSecond\":0,\"percSecond\":0,\"playing\":false,\"playingSecond\":false,\"secondsListened\":0,\"secondsListenedSecond\":0,\"expand\":false,\"source\":null,\"tracksQueue\":[],\"tracksPrevQueue\":[],\"favorite\":false,\"random\":false,\"repeat\":false,\"mute\":false,\"volume\":100,\"mode\":\"standard\"},\"ImportStore\":{\"url\":\"\",\"error\":false,\"focused\":false,\"loading\":false,\"loaded\":false,\"playlist\":null}}""></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チェックサムが異なります。
グーグルでここに来る人にとって、この問題に終わる奇妙な方法の1つは、同型レンダリングさえ使用しない(つまり、サーバー側で何もレンダリングしない)ことです。これは、HtmlWebpackPlugin
でテンプレートを使用してindex.html
ファイルを処理するときに起こりました。
私のindex.html
ファイルには既にbundle.js
ファイルを含めており、上記のプラグインにはscript-srcを介して別のbundle.js
も含まれています。 inject: false
をHtmlWebpackPlugin
コンストラクターに設定していることを確認してください。
私にとってnodejsを完全に殺し、再起動が働いた
[〜#〜] warning [〜#〜]ここでよくある答えは正しくありません。実行しているのは、既存のDOMを完全に削除し、クライアント上の新しいレンダリングに置き換えることです。これは、Reactからの高速で浅いレンダリングを失い、パフォーマンスを浪費しているため、OPエラーやその他のエラーが発生する可能性があります。
あなたの問題はCSSにあるようです-ここであなたの違いを説明するオートプレフィックスとインラインスタイルを使用している場合。
サーバー側はalign-items:centerをレンダリングし、クライアントはそれがWebkitブラウザーにあることを認識し、自動的に-webkit-align-itemsのプレフィックスを付けました。
CSSのセットアップ、およびインラインスタイルなどを使用するコンポーネントに関する詳細情報を投稿してください。
メインコンテンツをレイアウトコンポーネント内にレンダリングする場合、コンテンツのチェックサムがクライアントとサーバー間で一致するように、レイアウトを静的マークアップ(反応属性なし)としてレンダリングする必要があります。
サーバー:
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)
})
HtmlLayout:
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
私の場合、画面幅にアクセスできないときにコンポーネントが使用する「value」プロパティを渡さずに「react-responsive」からMediaQueryコンポーネントを使用していたという問題が原因でした(サーバー上など) )。
私が取り組んでいた同形アプリでこの問題に遭遇しました。私にとってうまくいったのは、信じられないかもしれませんが、キャッシュを空にしてChromeでアプリをハードリロードすることです。古いDOMが何らかの形でブラウザにキャッシュされているように見えました:)