response-router 4 - истории браузера требуется DOM

Я пытаюсь выполнить рендеринг на стороне сервера с помощью response-router 4. Я следую приведенному здесь примеру https://reacttraining.com/react-router/web/guides/server-rendering/putting-it-all-вместе

Согласно примеру на сервере мы должны использовать 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;

person Saf    schedule 28.03.2017    source источник
comment
Мне удалось это решить. Мне не нужно оборачивать мой ‹Route› внутри ‹BrowserRouter› в App.js. Потому что я уже оборачивал его ‹BrowserRouter› в clientIndex.js   -  person Saf    schedule 28.03.2017
comment
Я впервые занимаюсь маршрутизацией ... Думаю, вы сможете установить историю , используйте createMemoryHistory() и передайте его в свойство 'history' (т. е. <Router history={ history }>   -  person Jason Goemaat    schedule 29.03.2017
comment
@Saf большое спасибо ... ваше решение сработало как шарм, и я искал и настраивал идола изоморфной структуры, но не нашел ничего работающего ... ваш код качнулся .. !!   -  person Ritesh    schedule 06.04.2017


Ответы (3)


Вам нужно использовать другого поставщика истории для рендеринга на стороне сервера, потому что у вас нет реальной DOM (и истории браузера) на сервере. Таким образом, замена BrowserRouter на Router и альтернативный поставщик истории в вашем app.js может решить проблему. Также не обязательно использовать две обертки. Вы используете BrowserRouter дважды, в app.js, а также в clientIndex.js, что не нужно.

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();
}
person Rohith Rajasekharan    schedule 25.07.2018
comment
Я предполагаю, что isServer - это простой if statement, который проверяет, является ли он сервером или нет? - person Jamie Hutber; 27.11.2018
comment
Отлично, работает - спасибо - person Alex Karpov; 12.12.2020

В clientIndex.js

Вместо BrowserRouter используйте StaticRouter.

импортировать {BrowserRouter} из 'response-router-dom';

импортировать {StaticRouter} из 'response-router-dom'

person Kooldandy    schedule 11.01.2020
comment
изменение его на StaticRouter привело к сбою узла и моих часов webpack ... Не думаю, что это хорошее решение. - person Kaboukie; 21.02.2020

Как по существу отмечено в комментариях, можно столкнуться с этой ошибкой (как и у меня), если случайно обернуть компонент вашего приложения в <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.

person zgreen    schedule 11.07.2017
comment
это не отвечает на вопрос и довольно расплывчато, что такое Компонент? - person Paul Brunache; 06.09.2017
comment
Component - это компонент React, используемый функцией render. - person zgreen; 06.09.2017
comment
В вопросе отмечается ошибка Invariant Violation: Browser history needs a DOM, которая может возникнуть из-за обертывания <BrowserRouter> вокруг неправильного компонента. Это также отмечено здесь. - person zgreen; 06.09.2017