I'm trying to set up server-side rendering with the newest version of react-router v.4. I followed this tutorial https://react-router.now.sh/ServerRouter.
I get following error when I refresh browser: Invariant Violation: React.Children.only expected to receive a single React element child.
my routes.jsx file:
export default () =>
<div>
<Header />
<Match pattern="/" component={Home} />
<Match pattern="/about" component={About} />
<Miss component={NotFound} />
</div>;
and in index.jsx I'm rendering app
import BrowserRouter from 'react-router';
import Routes from './routes';
ReactDOM.render(<BrowserRouter> <Routes /> </BrowserRouter>, document.getElementById('app'));
Now as server I'm using express.js. Here is my configuration:
import Routes from '../routes';
server.use((req, res) => {
const context = createServerRenderContext();
let markup = renderToString(
<ServerRouter location={req.url} context={context} > <Routes /> </ServerRouter>);
const result = context.getResult();
if (result.redirect) {
res.writeHead(301, {
Location: result.redirect.pathname,
});
res.end();
} else {
if (result.missed) {
res.writeHead(404);
markup = renderToString(
<ServerRouter location={req.url} context={context}> <Routes /> </ServerRouter>);
}
res.write(markup);
res.end();
}
});
I didn't find any tutorial for server-rendering with this version of react-routes except official. Can anyone help me what I'm doing wrong ? thanks.
Solved !
First problem was that I had spaces around <Routes /> tag.
Correct solution:
<ServerRouter location={req.url} context={context}><Routes /></ServerRouter>);
Second problem was in included <Header /> tag in routes.jsx file.
I had the following error (Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of StatelessComponent)
File Header.jsx contained the following line of code:
import Link from 'react-router';
Correct solution: (I forgot to put curly brackets ):
import { Link } from 'react-router';
The big issue is that the<BrowserRouter> is only expected to have one child, so you should wrap it's children in a div. This is done so that React Router is environment agnostic (you can't render a div in React Native, so RR expects you to include the appropriate wrapper).
export default () =>
<BrowserRouter>
<div>
<Header />
<Match pattern="/" component={Home} />
<Match pattern="/about" component={About} />
<Miss component={NotFound} />
</div>
</BrowserRouter>;
As a secondary issue, you are including the <BrowserRouter> in your <App> component, so it will be rendered on the server. You do not want this. Only the <ServerRouter> should be rendered on the server. You should move the <BrowserRouter> further up your client side component hierarchy to avoid this.
// App
export default () =>
<div>
<Header />
<Match pattern="/" component={Home} />
<Match pattern="/about" component={About} />
<Miss component={NotFound} />
</div>;
// index.js
render((
<BrowserRouter>
<App />
</BrowserRouter>
), document.getElementById('app'))
because BrowserRouter doesn't exist on react-router, try install and import it from react-router-dom