反応ルーター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;
サーバー上に実際の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();
}
コメントに本質的に記載されているように、誤ってアプリコンポーネントを<BrowserRouter>
、代わりにラップする必要があるのはclientアプリです。
import React from 'react'
const App = () => <h1>Hello, World.</h1>
export default App
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 も参照してください。