Skip to content

Instantly share code, notes, and snippets.

@steve-taylor
Last active August 29, 2017 06:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steve-taylor/100ff147d26b2342854fc04e2208636f to your computer and use it in GitHub Desktop.
Save steve-taylor/100ff147d26b2342854fc04e2208636f to your computer and use it in GitHub Desktop.
Routing to React pages using Page.js
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>React + Bacon.js + Page.js</title>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script src="https://unpkg.com/react@latest/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@latest/dist/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bacon.js/0.7.95/Bacon.min.js"></script>
<script src="https://cdn.rawgit.com/visionmedia/page.js/master/page.js"></script>
</head>
<body>
<div id="react-app"></div>
<script type="text/babel" data-presets="es2015,stage-2,react">
const startRoutingBus = new Bacon.Bus();
const loginBus = new Bacon.Bus();
const logoutBus = new Bacon.Bus();
const LandingPage = () => (
<div>
Loading...
</div>
);
const HomePage = () => (
<div>
Home
</div>
);
const UserPage = ({username, givenName, familyName, onLogout}) => (
<div>
<table>
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Username</td>
<td>{username}</td>
</tr>
<tr>
<td>Given name</td>
<td>{givenName}</td>
</tr>
<tr>
<td>Family name</td>
<td>{familyName}</td>
</tr>
</tbody>
</table>
<div>
<button onClick={onLogout}>
Log out
</button>
</div>
</div>
);
const LoginPage = ({onLogin}) => (
<div>
<h2>Login</h2>
<div>
<button
onClick={() => {
setTimeout(() => onLogin({
username: 'another.user',
givenName: 'Another',
familyName: 'User'
}), 1000);
}}
>
Log in
</button>
</div>
</div>
);
const NotFoundPage = () => (
<div>
Not found
</div>
);
const userProp = Bacon.update(
null,
[loginBus], (state, {username, givenName, familyName}) => ({
username,
givenName,
familyName
}),
[logoutBus], () => null
);
loginBus.onValue(() => {
page('/user');
});
logoutBus.onValue(() => {
page('/login');
});
const appState = Bacon.combineTemplate({
user: userProp
// and so on...
});
const mountPoint = document.getElementById('react-app');
// Show landing page, wait for startRoutingBus, and start routing and combining the route stream with appState.
// (Probably could be broken up a bit, although this way, you can see how it all flows.)
Bacon
.later(0, <LandingPage/>)
.concat(
startRoutingBus.flatMapLatest(
() => appState.combine(
// Generate a stream from page.js mappings to page-level components.
Bacon.fromBinder(
sink => {
page('/', () => {
sink(() => (
<HomePage/>
));
});
page('/login', () => {
sink(() => (
<LoginPage
onLogin={({username, givenName, familyName}) => {
loginBus.push({username, givenName, familyName});
}}
/>
));
});
page('/user', () => {
sink(({user}) => (
<UserPage
{...user}
onLogout={() => {
logoutBus.push();
}}
/>
));
});
page('*', () => {
sink(() => (
<NotFoundPage/>
));
});
page();
return () => {
page.stop();
};
}
),
(app, Page) => (
<Page {...app}/>
)
)
)
)
.onValue(page => {
ReactDOM.render(page, mountPoint);
});
setTimeout(() => {
// Start mapping routes to the page stream. (Needs to start before pushing any app state.)
startRoutingBus.push();
// Push user info to the appState.user property.
loginBus.push({
username: 'john.smith',
givenName: 'John',
familyName: 'Smith'
});
}, 1000); // 1s delay to simulate loading current user from an endpoint
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment