Frontend reconciliation

The only thing that was missing from the last example was the client JavaScript code. The user wants to use the application and the server needs to deliver the client code bundle. How would this work? Routing has to work in the browser and on the server, without modification to the routes. In other words, the server handles routing in the initial request, then the browser takes over as the user starts clicking things and moving around in the application.

Let's create the index.js module for this example:

import React from 'react';
import { hydrate } from 'react-dom';

import App from './App';

hydrate(<App />, document.getElementById('root'));

This looks like most other index.js files that you've seen so far in this book. You render the <App> component in the root element in the HTML document. In this case, you're using the hydrate() function instead of the render() function. The two functions have the same end result — rendered JSX content in the browser window. The hydrate() function is different because it expects rendered component content to already be in place. This means that it will perform less work, because it will assume that the markup is correct and doesn't need to be updated on the initial render.

Only in development mode will React examine the entire DOM tree of the server-rendered content to make sure that the correct content is displayed. If there's a mismatch between the existing content and the output of the React components, you'll see warnings that show you where these mismatches happened so that you can go fix them.

Here is the App component that your app will render in the browser and on the Node.js server:

import React, { Component } from 'react';

export default class App extends Component {
state = { clicks: 0 };

render() {
return (
<section>
<header>
<h1>Hydrating The Client</h1>
</header>
<main>
<p>Clicks {this.state.clicks}</p>
<button
onClick={() =>
this.setState(state => ({ clicks: state.clicks + 1 }))
}
>
Click Me
</button>
</main>
</section>
);
}
}

The component renders a button that, when clicked, will update the clicks state. This state is rendered in a label above the button. When this component is rendered on the server, the default clicks value of 0 is used, and the onClick handler is ignored since it's just rendering static markup. Let's take a look at the server code next:

import fs from 'fs';
import React from 'react';
import { renderToString } from 'react-dom/server';
import express from 'express';

import App from './App';

const app = express();
const doc = fs.readFileSync('./build/index.html');

app.use(express.static('./build', { index: false }));

app.get('/*', (req, res) => {
const context = {};
const html = renderToString(<App />);

if (context.url) {
res.writeHead(301, {
Location: context.url
});
res.end();
} else {
res.write(
doc
.toString()
.replace('<div id="root">', `<div id="root">${html}`)
);
res.end();
}
});

app.listen(8080, () => {
console.log('Listening on 127.0.0.1:8080');
});

Let's walk through this source and see what's going on:

const doc = fs.readFileSync('./build/index.html');

This reads the index.html file that's created by your React build tool, such as create-react-app/react-scripts, and stores it in doc:

app.use(express.static('./build', { index: false }));

This tells the Express server to serve files under the ./build as static files, except for index.html. Instead, you're going to write a handler that responds to requests for the root of the site:

app.get('/*', (req, res) => {
const context = {};
const html = renderToString(<App />);

if (context.url) {
res.writeHead(301, {
Location: context.url
});
res.end();
} else {
res.write(
doc
.toString()
.replace('<div id="root">', `<div id="root">${html}`)
);
res.end();
}
});

This is where the html constant is populated with rendered React content. Then, it gets interpolated into the HTML string using replace() and is sent as the response. Because you've used the index.html file based on your build, it contains a link to the bundled React app that will run when loaded in the browser.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.252.140