I am trying to implement server-side-rendering in my react app (using the create-react-app environment). I am trying to follow the example shown here, which still maintaining my app's structure. Not much different, so I figured it wouldn't be an issue to follow along within my own app. https://medium.com/@benlu/ssr-with-create-react-app-v2-1b8b520681d9
When I attempt to build the app, I just the following: Invariant Violation: Browser history needs a DOM
I've looked everywhere I can and can't seem to find what I am doing wrong.
client side index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import Store from './store'
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
<Provider store={Store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
server side app.js
require('ignore-styles')
const bodyParser = require('body-parser')
const compression = require('compression')
const express = require('express')
const morgan = require('morgan')
const path = require('path')
const fs = require('fs')
require('babel-register')({
ignore: /\/(build|node_modules)\//,
presets: ['env', 'react-app']
})
// routes
const index = require('./routes/index')
// const api = require('./routes/api')
const universalLoader = require('./universal')
const app = express()
// Support Gzip
app.use(compression())
// Support post requests with body data (doesn't support multipart, use multer)
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
// Setup logger
app.use(morgan('combined'))
app.use('/', index)
// Serve static assets
app.use(express.static(path.resolve(__dirname, '..', 'build')))
// app.use('/api', api)
// Always return the main index.html, so react-router render the route in the client
app.use('/', universalLoader)
module.exports = app
server side universal.js
const path = require('path')
const fs = require('fs')
const React = require('react')
const {Provider} = require('react-redux')
const {renderToString} = require('react-dom/server')
const {StaticRouter} = require('react-router-dom')
const {default: configureStore} = require('../src/store')
const {default: App} = require('../src/App')
module.exports = function universalLoader(req, res) {
const filePath = path.resolve(__dirname, '..', 'build', 'index.html')
fs.readFile(filePath, 'utf8', (err, htmlData)=>{
if (err) {
console.error('read err', err)
return res.status(404).end()
}
const context = {}
const store = configureStore()
const markup = renderToString(
<Provider store={store}>
<StaticRouter
location={req.url}
context={context}
>
<App/>
</StaticRouter>
</Provider>
)
if (context.url) {
// Somewhere a `<Redirect>` was rendered
redirect(301, context.url)
} else {
// we're good, send the response
const RenderedApp = htmlData.replace('{{SSR}}', markup)
res.send(RenderedApp)
}
})
}