Nachdem wir im ersten Teil (Tutorial: Serverseitiges Rendering mit React – Teil 1: Einführung ) die grundlegende Konfiguration für unsere serverseitig gerenderte App umgesetzt haben, schauen wir uns in diesem Teil an, wie wir React Router auf dem Server nutzen können, um genau die Seiten zu rendern, die der User angefragt hat. Außerdem lernen wir, wie React Helmet für das Setzen von Meta-Tags auf dem Server konfiguriert werden muss.
2. React Router
Wir wollen sicherstellen, dass der User beim Abrufen der Seiten „/“, „/ueber-uns“, „/impressum“ etc. immer auch genau die richtigen statischen Inhalte vom Server geliefert bekommt. Im Client kümmert sich React Router darum, die richtigen Komponenten anzuzeigen, je nachdem, welche Route angesurft wird. Dieses Verhalten müssen wir nun auch auf dem Server ermöglichen.
Wenn wir React Router v4 in unseren bisherigen Projekt eingesetzt haben, dann kennen wir bereits das Modul BrowserRouter
. Wahrscheinlich enthält unsere clientseitige index.js im „src/“-Verzeichnis einen ähnlichen Code wie diesen hier:
1import React from 'react'; 2import ReactDOM from 'react-dom'; 3import { BrowserRouter } from 'react-router-dom'; 4import Routes from './Routes'; 5 6ReactDOM.render( 7 <BrowserRouter> 8 <Routes /> 9 </BrowserRouter>, 10 document.querySelector('#app') 11);
Dieser kann fast genau so bestehen bleiben. Wir ändern an dieser Stelle nur die Methode ReactDOM.render()
zu ReactDOM.hydrate()
, um zu verhindern, dass uns React folgende unschöne Warnmeldung in der Browserkonsole anzeigt:
"Warning: render(): Calling ReactDOM.render() to hydrate server-rendered markup will stop working in React v17. Replace the ReactDOM.render() call with ReactDOM.hydrate() if you want React to attach to the server HTML."
ReactDOM.hydrate()
ist genau für unseren Anwendungsfall zugeschnitten. Es geht von einem serverseitig gerenderten DOM aus und prüft lediglich, ob sich am bestehenden DOM etwas geändert hat.
In unserer server.js wollen wir nun auch das Routing integrieren, um immer die richtige statische HTML-Seite auf dem Server zu rendern.
1import express from 'express';
2import React from "react";
3import {StaticRouter} from "react-router-dom";import ReactDOM from 'react-dom/server';
4import Routes from "./src/Routes";
5const app = express();
6
7const renderer = req => { const content = ReactDOM.renderToString(
8 <StaticRouter location={req.path} context={{}}> <Routes /> </StaticRouter> );
9
10 return `
11 <html>
12 <head></head>
13 <body>
14 <div id="app">${content}</div>
15 <script src="bundle.js"></script>
16 </body>
17 </html>
18 `;
19 };
20
21 app.use(express.static('public'));
22 app.get('*', (req, res) => {
23 res.send(renderer(req)); });
24
25 app.listen(3000, () => {
26 console.log('Server gestartet auf http://localhost:3000');
27 });
Man sieht, dass wir innerhalb unserer renderToString()
-Methode anstelle von BrowserRouter
den StaticRouter
nutzen. Wir gehen hier auch noch einen Schritt weiter und übergeben dem StaticRouter
den Anfragepfad, der uns über req.path
von express
zur Verfügung gestellt wird. Damit weiß React Router, welche Seite gerendert werden soll und wir bekommen nicht immer die Startseite zurück geliefert. Der StaticRouter
benötigt immer ein context
-Property, den wir hier erstmal mit einem leeren Objekt definieren.
Mit diesen einfachen Änderungen haben wir erreicht, dass wir immer genau die Seite auf dem Server rendern, die der User anfragt. Als nächstes können wir uns um die Meta-Tags kümmern.
3. React Helmet Meta-Tags
React Helmet ist ein hilfreiches Tool, um für jede beliebige Page-Komponente Meta-Tags zu setzen. Normalerweise sieht eine Komponente, die React Helmet verwendet, so aus:
1...
2import { Helmet } from 'react-helmet';
3
4class ToDoList extends Component {
5...
6
7 render() {
8 return (
9 <div>
10 <Helmet>
11 <title>Mein Titel</title>
12 <meta name="description" content="Meine Beschreibung"/>
13 <meta property="og:title" content="Ein etwas anderer Titel" />
14 </Helmet>
15 ...
16 </div>
17 );
18 }
19}
React Helmet ist also selbst eine einfache Komponente, die wir innerhalb unserer (Page-)Komponenten integrieren. Man kann innerhalb von beliebige Meta-Tags definieren.
Beim SSR haben wir jedoch das Problem, dass diese Meta-Tags nicht ohne unser Zutun im
1import express from 'express';
2import React from "react";
3import {StaticRouter} from "react-router-dom";
4import ReactDOM from 'react-dom/server';
5import Routes from "./src/Routes";
6import { Helmet } from 'react-helmet';
7const app = express();
8
9const renderer = (req, store) => {
10 const content = ReactDOM.renderToString(
11 <StaticRouter location={req.path} context={{}}>
12 <Routes />
13 </StaticRouter>
14 );
15
16 const helmet = Helmet.renderStatic();
17 return `
18 <html>
19 <head>
20 ${helmet.title.toString()} ${helmet.meta.toString()} </head>
21 <body>
22 <div id="app">${content}</div>
23 <script src="bundle.js"></script>
24 </body>
25 </html>
26 `;
27};
28
29app.use(express.static('public'));
30app.get('*', (req, res) => {
31 res.send(renderer(req));
32});
33
34app.listen(3000, () => {
35 console.log('Server gestartet auf http://localhost:3000');
36});
Nach dem Aufruf von ReactDOM.renderToString()
enthält Helmet alle Informationen über die zu rendernden Meta-Tags. Deswegen können wir im Anschluss die Meta-Informationen einfach auslesen:
const helmet = Helmet.renderStatic();
Wie wir hier sehen, sind die Titel- und Meta-Informationen in zwei verschiedenen Properties am Helmet-Objekt verfügbar:
${helmet.title.toString()}
${helmet.meta.toString()}
Es gibt auch noch weitere Properties am Helmet-Objekt, die in der React Helmet Doku nachzulesen sind.
Durch diese kleine Anpassung sind nun auch unsere Meta-Tags im statisch gerenderten HTML enthalten.
Im letzten und umfangreichsten Teil der Serie (Tutorial: Serverseitiges Rendering mit React – Teil 3: Redux Store ) beleuchten wir, wie wir den Redux Store serverseitig konfigurieren. Wir sehen uns außerdem an, wie wir dynamische Inhalte laden bevor wir das statische HTML erzeugen und wie wir den befüllten Redux State an den Client übergeben.
Weitere Beiträge
von René Bohrenfeldt
Dein Job bei codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Weitere Artikel in diesem Themenbereich
Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog-Autor*in
René Bohrenfeldt
Agile Coach | People Lead
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.