Skip to content
This repository has been archived by the owner on Apr 29, 2023. It is now read-only.

Hyperapp#2 support #100

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open

Conversation

sergey-shpak
Copy link

@sergey-shpak sergey-shpak commented Dec 22, 2018

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

@ThobyV
Copy link

ThobyV commented Jan 4, 2019

For one I Really like The Consistency with the V1 API.

Good Job @sergey-shpak.

- Path-to-regexp usage (with cached paths)
- Router context passing
- Redirect as subscription
- Location 'back' and 'go' methods
- Browser history as subscription
Removed Rollup because it requires configuration and usage of multiple plugins (resolver, commonjs)
@sergey-shpak
Copy link
Author

  • 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')

src/Link.js Outdated Show resolved Hide resolved
Co-Authored-By: sergey-shpak <sergey.shpak.web@gmail.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
3 participants