Context As Undefined When Using React Router 4 For Server Side Rendering
I'm working on implementing server-side rendering with React Router 4 at a basic level to understand how it works. However, I am facing several problems and it is not clear in the React Router documentation how to solve them:
1) My context variable at / server / routes is undefined.
I've seen in previous tutorials that React Router used createServerRenderContext (); however, their documentation is not mentioned anymore, so I assume they got rid of it. I thought the StaticRouter should create your context for you when it renders? How can I define my context?
2) My html variable which renders the StaticRouter as a string shows these two errors in the console:
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it defined in.
Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it defined in.
I don't quite understand why this is a problem. I believe this is exported correctly and I am passing this through renderToString. Why is this giving me an error?
Any help is greatly appreciated! Here's my code below:
Server side
/server/server.js
import express from 'express'
import router from './routes/index.js'
const app = express();
app.use(express.static('public'));
app.use(router)
const PORT = 3000;
app.listen(PORT, function(){
console.log('Listening on ' + PORT)
});
/server/routes/index.js
import { Router } from 'express'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { StaticRouter } from 'react-router'
import App from '../../client/App'
const router = Router();
router.get('*', function(request, response) {
const context = {}
const html = ReactDOMServer.renderToString(
<StaticRouter location={request.url} context={context}>
<App/>
</StaticRouter>
);
if (context.url) {
response.writeHead(301, {
Location: context.url
})
response.end()
} else {
response.write(html)
response.end()
}
});
export default router
Client side
/client/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router';
import App from './App'
const props = window.PROPS;
ReactDOM.render(
<BrowserRouter>
<App {...props} />
</BrowserRouter>,
document
);
/client/App.js
import React from 'react'
class App extends React.Component {
constructor() {
super()
}
handleClick() {
alert()
}
render() {
return (
<html>
<head>
<title>{this.props.title}</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<main>
<h1>{this.props.title}</h1>
<p>This is some text that makes up a paragraph</p>
<button onClick={this.handleClick}>Click me</button>
</main>
<script dangerouslySetInnerHTML={{
__html: 'window.PROPS=' + JSON.stringify(this.props)
}} />
<script src="/bundle.js" />
</body>
</html>
)
}
}
export default App;
Webpack.config.js
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: './src/client/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'public'),
publicPath: '/public/'
},
module: {
rules: [
{
test: /\.js?$/,
exclude: /node_modules/,
include: path.join(__dirname, 'src'),
use: [
{
loader: 'babel-loader',
query: {
presets: ['react', ['es2015', { 'modules': false }], 'stage-0'],
plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy']
}
}
]
}
]
}
};
source to share
No one has answered this question yet
Check out similar questions: