HyperRouter
HyperRouter is a DSL wrapper for ReactRouter v4.x to provide client-side routing for Single Page Applications (SPA). As the user changes "pages" instead of reloading from the server your App will mount different components.

class AppRouter
include Hyperstack::Component
include Hyperstack::Router::Helpers
include Hyperstack::Router
render(DIV) do
UL do
LI { Link('/') { 'Home' } }
LI { Link('/about') { 'About' } }
end
Route('/', exact: true, mounts: Home)
Route('/about', mounts: About)
end
end
class Home
include Hyperstack::Component
render(DIV) do
H2 { 'Home' }
end
end
This is the Router module which you include in your top level component:
class MyRouter
include Hyperstack::Component
include Hyperstack::Router
end
With the base Router class, you can also specify the history you want to use.
This can be done either using a macro:
class MyRouter
include Hyperstack::Component
include Hyperstack::Router
history :browser # this is the default option if no other is specified
end
The macro accepts three options:
:browser
, :hash
, or :memory
.Or by defining the
history
method:class MyRouter
include Hyperstack::Component
include Hyperstack::Router
def history
self.class.browser_history
end
end
Use the
render
macro as normal. Note you cannot redefine the render
instance method in a Router componenentclass MyRouter
...
render(DIV) do
H1 { 'Hello world!' }
end
end
Routes are defined with special pseudo components you call inside the router/components. The router determines which of the routes to actually mount based on the current URL.
class MyRouter
...
render(DIV) do
Route('/', mounts: HelloWorld)
end
end
class HelloWorld
render do
H1 { 'Hello world!' }
end
end
The
Route
method takes a url path, and these options:mounts: Component
The component you want to mount when routed toexact: Boolean
When true, the path must match the location exactlystrict: Boolean
When true, the path will only match if the location and path both have/don't have a trailing slash
The
Route
method can also take a block instead of the mounts
option.class MyRouter
...
render(DIV) do
Route('/', exact: true) do
H1 { 'Hello world!' }
end
end
end
The block will be given the match, location, and history data:
class MyRouter
...
render(DIV) do
Route('/:name') do |match, location, history|
H1 { "Hello #{match.params[:name]} from #{location.pathname}, click me to go back!" }
.on(:click) { history.go_back }
end
end
end
- The
Hyperstack::Router::Helpers
is useful for components mounted by the router. - This automatically sets the
match
,location
, andhistory
params,and also gives you instance methods with those names. - You can use either
params.match
or justmatch
.and gives you access to theRoute
method and more. - This allows you to create inner routes as you need them.
class MyRouter
include Hyperstack::Component
include Hyperstack::Router::Helpers
include Hyperstack::Router
render(DIV) do
Route('/:name', mounts: Greet)
end
end
class Greet
include Hyperstack::Component
include Hyperstack::Router::Helpers
render(DIV) do
H1 { "Hello #{match.params[:foo]}!" }
Route(match.url, exact: true) do
H2 { 'What would you like to do?' }
end
Route("#{match.url}/:activity", mounts: Activity)
end
end
class Activity
include Hyperstack::Component
include Hyperstack::Router::Helpers
include Hyperstack::Router
render(DIV) do
H2 { params.match.params[:activity] }
end
end
Normally routes will always render alongside sibling routes that match as well.
class MyRouter
...
render(DIV) do
Route('/goodbye', mounts: Goodbye)
Route('/:name', mounts: Greet)
end
end
Going to
/goodbye
would match /:name
as well and render Greet
with the name
param with the value 'goodbye'. To avoid this behavior and only render one matching route at a time, use a Switch
component.class MyRouter
...
render(DIV) do
Switch do
Route('/goodbye', mounts: Goodbye)
Route('/:name', mounts: Greet)
end
end
end
Now, going to
/goodbye
would match the Goodbye
route first and only render that component.Links are provided by both the
Hyperstack::Router
and Hyperstack::Router::Helper
modules.The
Link
method takes a url path, and these options:search: String
adds the specified string to the search queryhash: String
adds the specified string to the hash locationit can also take a block of children to render inside it.
class MyRouter
...
render(DIV) do
Link('/Gregor Clegane')
Route('/', exact: true) { H1() }
Route('/:name') do |match|
H1 { "Will #{match.params[:name]} eat all the chickens?" }
end
end
end
NavLinks are the same as Links, but will add styling attributes when it matches the current url
active_class: String
adds the class to the link when the url matchesactive_style: String
adds the style to the link when the url matchesactive: Proc
A proc that will add extra logic to determine if the link is active
class MyRouter
...
render(DIV) do
NavLink('/Gregor Clegane', active_class: 'active-link')
NavLink('/Rodrik Cassel', active_style: { color: 'grey' })
NavLink('/Oberyn Martell',
active: ->(match, location) {
match && match.params[:name] && match.params[:name] =~ /Martell/
})
Route('/', exact: true) { H1() }
Route('/:name') do |match|
H1 { "Will #{match.params[:name]} eat all the chickens?" }
end
end
end
Pre-rendering is automatically taken care for you under the hood.
To setup HyperRouter:
- Install the gem
- Your page should render your router as its top-level-component (first component to be rendered on the page) - in the example below this would be
AppRouter
- You will need to configure your server to route all unknown routes to the client-side router (Rails example below)
Assuming your router is called
AppRouter
, add the following to your routes.rb
root 'Hyperstack#AppRouter' # see note below
match '*all', to: 'Hyperstack#AppRouter', via: [:get] # this should be the last line of routes.rb
Note:
root 'Hyperstack#AppRouter'
is shorthand which will automagically create a Controller, View and launch AppRouter
as the top-level Component. If you are rendering your Component via your own COntroller or View then ignore this line.import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
const BasicExample = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</div>
</Router>
)
const Home = () => (
<div>
<h2>Home</h2>
</div>
)
const About = () => (
<div>
<h2>About</h2>
</div>
)
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<ul>
<li><Link to={`${match.url}/rendering`}>Rendering with React</Link></li>
<li><Link to={`${match.url}/components`}>Components</Link></li>
<li><Link to={`${match.url}/props-v-state`}>Props v. State</Link></li>
</ul>
<Route path={`${match.url}/:topicId`} component={Topic}/>
<Route exact path={match.url} render={() => (
<h3>Please select a topic.</h3>
)}/>
</div>
)
const Topic = ({ match }) => (
<div>
<h3>{match.params.topicId}</h3>
</div>
)
export default BasicExample
And here is the same example in Hyperstack:
class BasicExample
include Hyperstack::Component
include Hyperstack::Router::Helpers
include Hyperstack::Router
render(DIV) do
UL do
LI { Link('/') { 'Home' } }
LI { Link('/about') { 'About' } }
LI { Link('/topics') { 'Topics' } }
end
Route('/', exact: true, mounts: Home)
Route('/about', mounts: About)
Route('/topics', mounts: Topics)
end
end
class Home
include Hyperstack::Component
include Hyperstack::Router::Helpers
render(DIV) do
H2 { 'Home' }
end
end
class About
include Hyperstack::Component
include Hyperstack::Router::Helpers
render(:div) do
H2 { 'About' }
end
end
class Topics
include Hyperstack::Component
include Hyperstack::Router::Helpers
render(DIV) do
H2 { 'Topics' }
UL() do
LI { Link("#{match.url}/rendering") { 'Rendering with React' } }
LI { Link("#{match.url}/components") { 'Components' } }
LI { Link("#{match.url}/props-v-state") { 'Props v. State' } }
end
Route("#{match.url}/:topic_id", mounts: Topic)
Route(match.url, exact: true) do
H3 { 'Please select a topic.' }
end
end
end
class Topic
include Hyperstack::Component
include Hyperstack::Router::Helpers
render(:div) do
H3 { match.params[:topic_id] }
end
end
Last modified 2yr ago