diff --git a/docs/isomorphic-routing-with-react-and-express.md b/docs/isomorphic-routing-with-react-and-express.md new file mode 100644 index 0000000..e9849d0 --- /dev/null +++ b/docs/isomorphic-routing-with-react-and-express.md @@ -0,0 +1,158 @@ +# Isomorphic (universal) routing with React and Express + +```js +// src/history.js +import createBrowserHistory from 'history/createBrowserHistory'; +export default createBrowserHistory(); +``` + +```jsx +// src/routes.js +import React from 'react'; +import history from './history'; + +function onClick(event) { + event.preventDefault(); // prevent full page reload + history.push(event.currentTarget.getAttribute('href')); // do SPA navigation +} + +export default { + path: '/', + children: [ + { + path: '/', + action() { + return { + title: 'Welcome!', + component: ( +
+

Hello, World!

+ +
+ ), + }; + }, + }, + { + path: '/about', + action() { + return { + title: 'About Us', + component:

About Us

, + }; + }, + }, + { + path: '/users', + children: [ + { + path: '/', + async action() { + const users = await fetch('/api/users'); + return { + title: 'Users', + component: , + }; + }, + }, + { + path: '/:user', + async action({ params }) { + const user = await fetch(`/api/user/${params.user}`); + return { + title: user.name, + component: , + }; + }, + } + ], + }, + { + path: '/articles', + async action() { + const articles = await fetch( + 'https://gist.githubusercontent.com/koistya/a32919e847531320675764e7308b796a' + + '/raw/fcbb12e60f8e664240c66bedd747978b16781231/articles.json' + ); + return { + title: 'Articles', + component: ( +
+

Articles

+ +
+ ), + }; + }, + }, + { + path: '*', // wildcard route (must go last) + action() { + return { + title: 'Page Not Found', + component:

Not Found

, + }; + }, + }, + ], +}; +``` + +```js +// src/client.js +import UniversalRouter from 'universal-router'; +import ReactDOM from 'react-dom'; +import history from './history'; +import routes from './routes'; + +function render(location) { + UniversalRouter.resolve(routes, location.pathname).then(route => { + document.title = route.title; + ReactDOM.render(route.component, document.body); + }); +} + +render(history.location); // initialize the app +history.listen(location => render); // listen for client-side navigation +``` + +```js +// src/server.js +import 'node-fetch'; // Fetch polyfill +import express from 'express'; +import UniversalRouter from 'universal-router'; +import ReactDOM from 'react-dom/server'; +import routes from './routes'; + +const app = express(); +const port = 3000; + +app.get('*', (req, res, next) => { + UniversalRouter.resolve(routes, req.path).then(route => { + res.send( + ` + + ${route.title} + ${ReactDOM.renderToString(route.component)} + ` + ); + }); +}); + +app.listen(port, () => { + console.log(`The server is running at http://localhost:${port}/`); +}); +```