LoginSignup
9
7

More than 5 years have passed since last update.

React Routerをサーバーサイドとクライアントサイドで同期させる時の覚書

Posted at

地味にハマったのでメモ。

通常のReact Routerの書き方

client-react-router.jsx
import { render } from 'react';
import { Router } from 'react-router';

import routes from 'YOUR_ROUTES_FILE_PATH';
const mountNode = document.getElementById('app');

render(
    <Router history={history} routes={routes} />, 
    mountNode
);

サーバーサイドでReact Routerを使う時の書き方

Expressのコードも混ざってますが、だいたいこんな感じ。

server-react-router.jsx
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import Express from 'express';

import routes from 'YOUR_ROUTES_FILE_PATH';

const app = new Express();
const port = 3000;

app.use(handleRender);
app.listen( port, ( error ) => {
    if ( error ) {
        console.error( error );
    } else {
        console.info(`Listening on port ${port}` ); 
    }
});

function handlRender(req, res ) => {
    match({ routes, location: req.url }, ( error, redirectLocation, renderProps ) => {
        if ( error ) {
            res.status( 500 ).send( error.message );
        } else if ( redirectLocation ) {
            res.redirect( 302, redirectLocation.pathname + redirectLocation.search );
        } else if ( renderProps ) {
            res.status( 200 ).send( renderToString( <RouterContext {...renderProps} />));
        } else {
            res.status( 404 ).send( 'Not Found' );
        }
    });
}

二つを組み合わせると・・・

こういうエラーがでる。

client-error.log
warning.js:45Warning: 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) .1.2"> items</span><span data-reactid=".
 (server) .1.2"> items</span></p></div>

IndexRouteのページではエラーにならず、子ページで発生した。
下の例だと「/sample」にアクセスした時に、クライアントサイドがIndexRouteのコンポーネント(App)をレンダリングしようしてエラーになる。

route-sample.jsx
    <Route path='/' component={RouterLayout}>
        <IndexRoute component={App} />
        <Route path='sample' component={Sample} />
    </Route>

クライアントサイドのRouterを書き換える

解決方法はドキュメントに思いっきり書いてあった
https://github.com/reactjs/react-router/blob/master/docs/guides/ServerRendering.md#async-routes

クライアント側のRouterを以下のように書き換えればOK。

fixed-client-react-router.jsx
import { render } from 'react';
import { match, Router } from 'react-router';

import routes from 'YOUR_ROUTES_FILE_PATH';
const mountNode = document.getElementById('app');

match( { history, routes }, ( error, redirectLocation, renderProps ) => {
    render(
        <Router history={history} routes={routes} />, 
        mountNode
    );
});

とりあえずこれでクライアントサイドが意図せぬコンポーネントをレンダリングしようとする問題は解決した。

9
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7