hyperapp-router
hyperapp-router copied to clipboard
Hyperapp#2 support
Hyperapp#2 Support (Updating)
Implemented with state-first in mind, simplicity and consistency to hav#1 router.
This Router follows simple concept of state-driven components (as hyperapp itself), the only one possible way to route component is to properly update application state (location). Router doesn't tightly depend on window.history anymore, that means you can use multiple applications at once.
Router internally uses 'path-to-regexp' syntax to compile paths this brings more routing power and consistency with other frameworks.
Quick start
Since routing depends on state, 'state.location' should be defined at first. Each 'Route' component should receive 'location' property to process routes. Link component is used to properly update 'state.location'.
import { app, h } from 'hyperapp'
import { location, Link, Route } from '@hyperapp/router'
app({
init: [location, '/foo'],
view: state => <div>
<nav>
<Link to='/foo'>Foo</Link>
<Link to='/bar'>Bar</Link>
</nav>
<main>
<Route path='/foo' location={ state.location }>
<div>Foo</div>
</Route>
<Route path='/bar' location={ state.location }>
<div>Bar</div>
</Route>
</main>
</div>,
container: document.body
})
- Route 'render' property is more commonly used to render nested routes, more details at Route Params
Usage Examples
Base example
Using Route 'render' property and nested 'Route' component
import { app, h } from 'hyperapp'
import { location, Link, Route } from '@hyperapp/router'
const Home = () =>
<div>Home</div>
const Blog = () =>
<div>Blog</div>
const About = () =>
<div>About</div>
app({
init: [location, '/home'],
view: state => <div>
<nav><li>
<Link to='/home'>Home</Link>
</li><li>
<Link to='/blog'>Blog</Link>
</li><li>
<Link to='/about'>About</Link>
</li></nav>
<main>
<Route render={(props, Route) =>
<Route path='/home' render={ Home } />
|| <Route path='/blog' render={ Blog } />
|| <Route path='/about' render={ About } />
} location={ state.location } />
</main>
</div>,
container: document.body
})
- Please notice proper 'switch' usage with logical operator
||
Route Parameters
Using nested 'Route' component for nested routes, route context (to pass properties to nested components and routes), using route params
import { app, h } from 'hyperapp'
import { location, Link, Route } from '@hyperapp/router'
const Home = () =>
<div>Home</div>
const Blog = ({ posts }) => {
const list = Object.entries(posts)
return <div>
<h5>Blog posts</h5>
<ul>{ list.map(([id, post]) =>
<li><Link to={`/blog/${id}`} >{ id }</Link></li>
)}</ul>
</div>
}
const Post = ({ route }) => <div>
<Link onClick={location.back}>Back</Link> Post: { route.params.id }
</div>
app({
// State composition
init: () => {
const state = {
posts: {
'post1': { text: 'Post#1 Text' },
'post2': { text: 'Post#1 Text' },
}
}
return location(state, '/home')
},
view: state => <div>
<nav><li>
<Link to='/home'>Home</Link>
</li><li>
<Link to='/blog'>Blog</Link>
</li></nav>
<main>
<Route render={(props, Route) =>
<Route path='/blog' render={ (props, Route) => [
<Blog posts={ props.route.context.state.posts } />,
<Route path='/:id' render={ Post } />
]} />
|| <Route path='/home' render={ Home } />
} location={ state.location } state={ state } />
</main>
</div>,
container: document.body
})
- Please notice 'location.back' action usage`
Redirects
Redirect as subscription
import { app, h } from 'hyperapp'
import { Redirect, location, Link, Route } from '@hyperapp/router'
const Home = () =>
<div>Home</div>
const Blog = () =>
<div>Blog</div>
app({
init: [location, '/home'],
view: state => <div>
<nav><li>
<Link to='/home'>Home</Link>
</li><li>
<Link to='/blog'>Blog</Link>
</li></nav>
<main>
<Route render={(props, Route) =>
<Route path='/home' render={ Home } />
|| <Route path='/blog' render={ Blog } />
} location={ state.location } />
</main>
</div>,
subscriptions: state => [
<Redirect from='/home' to='/blog' location={ state.location } />
],
container: document.body
})
No Match (Default)
import { app, h } from 'hyperapp'
import { Redirect, location, Link, Route } from '@hyperapp/router'
const Home = () =>
<div>Home</div>
const NotFound = () =>
<div>404</div>
app({
init: [location, '/home'],
view: state => <div>
<nav><li>
<Link to='/home'>Home</Link>
</li><li>
<Link to='/some-path'>Lost</Link>
</li></nav>
<main>
<Route render={(props, Route) =>
<Route path='/home' render={ Home } />
|| <Route render={ NotFound } />
} location={ state.location } />
</main>
</div>,
container: document.body
})
History
If you want to expose state.location
and subscribe to window.history,
use 'history' as subscription
import { app, h } from 'hyperapp'
import { history, location, Link, Route } from '@hyperapp/router'
const Home = () =>
<div>Home</div>
const Blog= () =>
<div>Blog</div>
app({
init: [location, '/home'],
view: state => <div>
<nav><li>
<Link to='/home'>Home</Link>
</li><li>
<Link to='/blog'>Blog</Link>
</li></nav>
<main>
<Route render={(props, Route) =>
<Route path='/home' render={ Home } />
|| <Route path='/blog' render={ Blog } />
} location={ state.location } />
</main>
</div>,
subscriptions: state => [
history(state.location)
],
container: document.body
})
Router#v2 API
location
location.back
location.go
history
Link
Route
Redirect
For one I Really like The Consistency with the V1 API.
Good Job @sergey-shpak.
- installing router from github as
"@hyperapp/router": "github:sergey-shpak/router#V2"
builds package (npm prepare
) but doesn't keep router dependencies, so if you're importing router as a 'module' please install required deps ('path-to-regexp')