Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add code recipes #86

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions docs/isomorphic-routing-with-react-and-express.md
Original file line number Diff line number Diff line change
@@ -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: (
<main>
<h1>Hello, World!</h1>
<nav>
Navigation:
<ul>
<li><a href="/" onClick={onClick}>Home</a></li>
<li><a href="/about" onClick={onClick}>About Us</a></li>
<li><a href="/users" onClick={onClick}>Users</a></li>
<li><a href="/articles" onClick={onClick}>Articles</a></li>
<li><a href="/404" onClick={onClick}>404</a></li>
</ul>
</nav>
</main>
),
};
},
},
{
path: '/about',
action() {
return {
title: 'About Us',
component: <h1>About Us</h1>,
};
},
},
{
path: '/users',
children: [
{
path: '/',
async action() {
const users = await fetch('/api/users');
return {
title: 'Users',
component: <Users users={users} />,
};
},
},
{
path: '/:user',
async action({ params }) {
const user = await fetch(`/api/user/${params.user}`);
return {
title: user.name,
component: <User user={user} />,
};
},
}
],
},
{
path: '/articles',
async action() {
const articles = await fetch(
'https://gist.githubusercontent.com/koistya/a32919e847531320675764e7308b796a' +
'/raw/fcbb12e60f8e664240c66bedd747978b16781231/articles.json'
);
return {
title: 'Articles',
component: (
<main>
<h1>Articles</h1>
<ul>
{articles.map(article =>
<li><a href={article.url}>{article.title}</a></li>
)}
</ul>
</main>
),
};
},
},
{
path: '*', // wildcard route (must go last)
action() {
return {
title: 'Page Not Found',
component: <h1>Not Found</h1>,
};
},
},
],
};
```

```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 => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have this api for now ?
I like this design, for now resolve is an prototype method of class UniversalRouter, and we need to call new UniversalRouter to use resolve

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(
`<!doctype html>
<html>
<head><title>${route.title}</title></head>
<body>${ReactDOM.renderToString(route.component)}</body>
</html>`
);
});
});

app.listen(port, () => {
console.log(`The server is running at http://localhost:${port}/`);
});
```