web-dev-qa-db-ja.com

react-router 4-ブラウザーの履歴にはDOMが必要

反応ルーター4を使用してサーバー側のレンダリングを試みています。ここで提供されている例 に従っています。一緒に

サーバーの例に従って、StaticRouterを使用する必要があります。例に従ってインポートすると、StaticRouterが未定義として表示されます

import {StaticRouter} from 'react-router';

オンラインでいくつかの調査を行った後、react-router-dom。インポートステートメントは次のようになります。

import {StaticRouter} from 'react-router-dom';

ただし、コードを実行すると、Invariant Violation: Browser history needs a DOMブラウザで。

私のserver.jsファイルコード

....
app.get( '*', ( req, res ) => {
  const html = fs.readFileSync(path.resolve(__dirname, '../index.html')).toString();
  const context = {};
  const markup = ReactDOMServer.renderToString(
    <StaticRouter location={req.url} context={context} >
      <App/>
    </StaticRouter>
  );

  if (context.url) {
    res.writeHead(302, {
      Location: context.url
    })
    res.end();
  } else {
      res.send(html.replace('$react', markup));
  }
} );
....

そして、私のclient/index.jsコード

....
ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), root);
....

v1を更新例を最小限に抑え、同じエラーが引き続き発生するようにしました。

clientIndex.js

import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import App from '../App'

ReactDOM.render((
  <BrowserRouter>
    <App/>
  </BrowserRouter>
), document.getElementById('app'))

serverIndex.js

import { createServer } from 'http'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { StaticRouter } from 'react-router'
import App from '../App'

createServer((req, res) => {
  const context = {}

  const html = ReactDOMServer.renderToString(
    <StaticRouter
      location={req.url}
      context={context}
    >
      <App/>
    </StaticRouter>
  )

res.write(`
  <!doctype html>
  <div id="app">${html}</div>
`)
res.end()
}).listen(3000);

App.js

import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import routes from "./client/routes";
const App = ( ) => (
  <Router>
    <Route path="/" exact render={( props ) => ( <div>Helloworld</div> )} />
  </Router>
)

export default App;
18
Saf

サーバー上に実際のDOM(およびブラウザーの履歴)がないため、サーバー側のレンダリングには異なる履歴プロバイダーを使用する必要があります。したがって、BrowserRouterをルーターとapp.jsの代替履歴プロバイダーに置き換えることで問題を解決できます。また、2つのラッパーを使用する必要はありません。 app.jsと不要なclientIndex.jsでBrowserRouterを2回使用しています。

import { Route, Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';

const history = createMemoryHistory();

  <Router history={history}>
   <Route path="/" exact render={( props ) => ( <div>Helloworld</div> )} />
  </Router>

StaticRouterを、クライアントとサーバーの両方で使用できるConnectedRouterに置き換えることができます。次のコードを使用して履歴を選択し、ConnectedRouterの履歴で使用するためにエクスポートします。

export default (url = '/') => {
// Create a history depending on the environment
  const history = isServer
    ? createMemoryHistory({
        initialEntries: [url]
     })
   : createBrowserHistory();
}
11

コメントに本質的に記載されているように、誤ってアプリコンポーネントを<BrowserRouter>、代わりにラップする必要があるのはclientアプリです。

App.js

import React from 'react'

const App = () => <h1>Hello, World.</h1>

export default App

ClientApp.js

import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import ReactDOM from 'react-dom'
import App from './App'

const render = Component => {
  ReactDOM.render(
    <BrowserRouter>
      <Component />
    </BrowserRouter>,
    document.getElementById('app')
  )
}

render(App)

React Router docs も参照してください。

5
zgreen