@app-elements/router

3.4.1 • Public • Published

Router

Provides a <Router /> component that accepts an object definition of routes, conditionally rendering them when the URL matches their path. It also supports nested Routers, a <Link /> component, and a useRouter hook to interact with the router.

Related components/hooks

Installation

npm install --save @app-elements/router

Usage

import { RouteProvider, Router } from '@app-elements/router'

// import your top-level routes (details about the routes object below)
import routes from './routes'

// ...
// The RouteProvider and top-most Router should both be provided the same
// top-level routes object.
<RouteProvider routes={routes}>
  <Router routes={routes} />
</RouteProvider>

Defining Your Routes

export const routes = {
  home: {
    path: '/',
    component: Home
  },
  users: {
    path: '/users',
    component: Users
  },
  user: {
    path: '/users/:id',
    component: User
  },
}

When a path is matched, the corresponding component will be rendered. The key for each object (home, users, user), is the name of the route.

Dynamic Values From The URL

If you need to parse the data out of the URL, use a dynamic segment (they start with a :). The parsed value will become a prop sent to the matched component.

In the above example, {id} would be a prop on the <User /> component.

Nested Routers

If you want to group certain routes together, you can define multiple Routers. This allows you to, for instance, render a common header or navigation component for a certain grouping of routes. To nest routes, you need to define parent routes. Parent routes look like so:

export default {
  marketing: {
    routes: marketingRoutes,
    component: Marketing
  },
  account: {
    routes: accountRoutes,
    component: Account
  }
}

You'll notice the difference is that each route object has a routes property instead of a path property. If any of the nested routes match the current URL, then that parent routes' component will render.

Let's say the accountRoutes are something like:

export const accountRoutes = {
  login: {
    path: '/login',
    component: Login
  },
  signup: {
    path: '/signup',
    component: SignUp
  }
}

And the current URL is: /signup, then the parent route account will match, and the <Account /> component will render. The last step is to include a <Router /> inside the <Account /> component that gets passed the accountRoutes object. As an example, <Account /> could look like this:

import accountRoutes from './routes'

// If we wanted to render some navigation links on *all* account routes,
// we would render them inside this `Account` component.
import AccountNav from './AccountNav'

export const Account = () => (
  <div>
    <AccountNav />
    <Router routes={accountRoutes} />
  </div>
)

Now you have a top-level router that renders different components based on nested routes. Those top-level, or parent route components can then include a nested <Router /> to gain finer control over what gets rendered based on the current URL.

Router Props

Prop Type Default Description
routes Object None An object of objects representing the routes. Supported keys are path, component, and routes.

StackRouter

<StackRouter /> builds on <Router /> by maintaining a "stack" or history of the rendered components, and exposing that stack to a Function as Child. From there you can determine how you want to manage or render the stack. For instance, by wrapping the current active component/route in the stack with react-transition-group components, you can easily add animations to your route transitions.

import { StackRouter } from '@app-elements/router'
import { CSSTransition, TransitionGroup } from "react-transition-group"

// ...
  {/*
    The current active route is always the last in the `stack`
    array. In this case, we are going to limit the stack to only
    hold 1 route, and just utilize the function as child pattern,
    so we can wrap the route component with a TransitionGroup.
  */}
  <StackRouter routes={routes}>
    {({ stack, limit = 1 }) => {
      const { path, args, isBack, Component } = stack[stack.length - 1]
      return (
        <TransitionGroup className="stack">
          <CSSTransition
            key={path}
            classNames={isBack ? "fade-reverse" : "fade"}
            addEndListener={(node, done) => {
              node.addEventListener("transitionend", done, false)
            }}
          >
            <div className='page'>
              <Component {...args} />
            </div>
          </CSSTransition>
        </TransitionGroup>
      )
    }}
  </StackRouter>

Link

import { Link } from '@app-elements/router'

// Render an anchor with a named route
return <Link to='post' args={{ id: post.id }}>{post.title}</Link>

RouteTo

import { RouteTo } from '@app-elements/router'

// In your component (perhaps when a form success state is reached) 
// you can render RouteTo to route to a new URL.
return <RouteTo name='blogPost' args={{ id }} />

Link and RouteTo Props

Prop Type Default Description
to String None String that matches a key in your routes object.
name String None Same as to, for backwards compatibility/preference.
args Object None Object of key-value pairs to replace dynamic values in a route definition. Ex. posts/:id => { id: 1 }
queries Object None Object of key-value pairs to convert to querystring params.

SyncRouterState

import { SyncRouterState } from '@app-elements/router'

// If you wish to sync the router state to your own global state (redux, atom, mobx, etc.)
// {
//   route: {
//     name: "post",
//     path: "/posts/:id",
//     args: { id: 1 }
//   },
//   currentPath: "/posts/1"
// }
return (
  <SyncRouterState>
    {state => console.log("SyncRouterState", state)}
  </SyncRouterState>
)

useRouter

import { useRouter } from '@app-elements/router'

// Access some of the internal functions of the router
const {
  path,
  setPath,
  routeTo,
  route,
  setRoute
} = useRouter()

useRouter props

Prop Type Description
path String Current path (defaults to window.location.pathname + window.location.search
setPath Function Programmatically set the path
routeTo Function Perform a "route change", calling history.pushState and setPath
route Object The current matched route. Example values
setRoute Function Programmatically set the route. Only sets if the name does not match current route.name

useScrollToTop

We're trying not to make too many assumptions, so the Router does not scroll to top automatically. But, there is a super simple hook to enable such behavior.

import { useScrollToTop } from '@app-elements/router'

// ... in your top-level component
useScrollToTop()

// also returns a ref, if you would like to scroll a specific element to the
// top, rather than the whole window
const ref = useScrollToTop()

<div id="main-nav" ref={ref}>

Package Sidebar

Install

npm i @app-elements/router

Weekly Downloads

58

Version

3.4.1

License

ISC

Unpacked Size

190 kB

Total Files

15

Last publish

Collaborators

  • coryschadt
  • trustfall
  • adriaanwm
  • staydecent