Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

router-spa-react

romagny134MIT0.0.1TypeScript support: included

React Spa Router

react-spa-router, react, spa, sparouter, router

readme

React Spa Router

Build Status

Router for React applications:

  • 2 modes : html5 history,hash
  • Page animation/transition with css or js
  • Active elements
  • Route guards
  • Child routes
  • Named views
  • Actions

Installation

npm i react-spa-router -S

Imports

import { Router } from 'react-spa-router';

Router & route configs

Router config Description
routes routes array
mode hash (by default) and history (html5 history).
scroll handle navigation to fragment (true by default)
Route config Description
path the path pattern ("/posts" or "posts/:id" or "/posts/:id([a-z]+)" for example)
name route name
action an action
actions an array of actions
data extra data to pass
canActivate route guards
canDeactivate route guards
redirectTo redirect to route url
children nested routes

Example create routes

const routes = [
    { path: '/', action: () => viewRender(<Home />) },
    { path: '/posts', action: () => viewRender(<PostList />), canActivate: [MyGuard], canDeactivate: [MyGuard] },
    { path: '/posts/:id', action: ({ route }) => viewRender(<PostDetail id={route.params.id} />) },
    { name: 'about', path: '/about', action: () => viewRender(<About />) },
    {
        path: 'customers', action: () => viewRender(<Customers />, 'customers'),
        children: [
            {
                path: '',
                actions: [
                    () => viewRender(<CustomerList />, 'top'),
                    () => viewRender(<CustomerDetail />, 'bottom'),
                    ({ router }) => console.log('Activate customers', router)
                ]
            },
            {
                path: ':id',
                actions: [
                    () => viewRender(<CustomerList />, 'top'),
                    ({ router, route }) => viewRender(<CustomerDetail id={route.params.id} />, 'bottom'),
                    () => console.log('Activate customer detail')
                ]
            }]
    },
    { path: '**', redirectTo: '/' }
];

An action return the previous promise result. Example:

const routes = [
    {
        path: '/', actions: [
            () => 'My result',
            ({ router, route, result }) => console.log(router, route, result)
        ]
    }
];

Create router (in App component)

class App extends React.Component<any, any> {
    constructor(props) {
        super(props);

        const router = new Router({
            mode: 'history',
            routes
        }).run((route) => {
            // console.log(route);
        }, (err) => {
            console.warn('error', err);
        });
    }

    render() {
        return (
            <div className='container'>
                <nav>
                    <ul>
                        <Link tag='li' to='/' activeClassName='active'>Home</Link>
                        <Link tag='li' to='/posts' activeClassName='active'>Post list</Link>
                        <Link tag='li' to='/posts/10' activeClassName='active'>Detail</Link>
                        <Link tag='li' to={{ path: '/posts/50', query: { q: 'mysearch' }, fragment: '#section1' }} activeClassName='active' activePattern={/\/posts\/[0-9]+/}>Query+fragment and active pattern</Link>
                        <Link tag='li' to='/customers' activeClassName='active' exact={false}>Customers</Link>
                        {/* named route */}
                        <Link tag='li' to={{ name: 'about' }} activeClassName='active'>About</Link>
                    </ul>
                </nav>
                <RouterView className={this.state.selectedValue} enter='navInPrev' leave='navOutPrev' enterTimeout={500} leaveTimeout={500} simultaneous={true} />
                 {/* named view*/}
                 <RouterView name='customers' />
            </div >
        );
    }
}

With path

<Link to='/'>Home</Link>
{/* with params */}
<Link to='/posts/10'>Detail</Link>
{/* with query and fragment */}
<Link to='/posts/10?q=abc&cat=10#section1'>Detail</Link>

Or

 <Link to={{ path: '/posts/10', query: { q: 'mysearch' }, fragment: '#section1' }}>Detail</Link>

Named route

<Link to={{ name: 'about' }} activeClassName='active'>About</Link>

activeClassName

<Link to='/posts/10' activeClassName='active'>Detail</Link>
.active {
    color: orange;
}

exact (by default is true)

Set exact to false

<Link to='/customers' activeClassName='active' exact={false}>Customers</Link>

Pattern

Allow to define a regex to check active

<Link to='/posts/10' activeClassName='active' activePattern={/\/posts\/[0-9]+/}>Detail/Link>

Example link will be append to a li element. The activeClassName is added to li element.

 <ul>
    <Link tag='li' to='/' activeClassName='active'>Home</Link>
</ul>

RouterView

Is the container for the "router-pages"

Default RouterView

 <RouterView />

Named RouterView

 <RouterView name='my-view' />

Change component of a view with "viewRender" function

viewRender(<Home />)
{/* or */}
viewRender(<Home />, 'default')

Change the content of a named view

viewRender(<Home />, 'my-view')
router.navigateToUrl('/posts/10?q=abc&cat=10#section1');
// or with named route (route name, params, query, fragment)
router.navigateTo('post-list', { id :10 }, { q: 'abc', cat: 10 }, '#section1');
  • replace, replaceUrl, goBack, goForward

Animate RouterView

Example a fadeIn, fadeOut

<RouterView enter='fadeIn' enterTimeout={1000} leave='fadeOut' leaveTimeout={1000} />
@keyframes fadeIn {
    from { opacity: 0; }
    to {  opacity: 1;  }
}

@keyframes fadeOut {
    from {  opacity: 1; }
    to {  opacity: 0; }
}

.fadeIn {
    animation: 1s linear 0s fadeIn forwards;
}

.fadeOut {
    animation: 1s linear 0s fadeOut forwards;
}

.router-page {
    opacity: 0;
}

.router-page.current {
    opacity: 1;
}

simultaneous animation

<RouterView className='fxShuffle' enter='navInPrev' leave='navOutPrev' enterTimeout={500} leaveTimeout={500} simultaneous={true} />

JavaScript animation

Control animation with beforeEach, afterEach and error callback (page not found, navigation aborted with a guard)

const router = new Router({routes}).beforeEach((next) => {
    // play animation
    next();
}).afterEach(() => {
    // play end animation
}).run(()=>{

}, ()=>{
    // on error
});

Route Guards

Create a class

class MyGuard {
    canActivate(route, next) {
        let result = confirm('Activate?');
        next(result);
    }
    canDeactivate(activeComponents, route, next) {
        let component = activeComponents['PostList'];
        let result = component && component.checkCanDeactivate ? component.checkCanDeactivate() : true;
        next(result);
    }
}

Add the guard to check can activate and can deactivate a route

const routes = [
    { path: '/', action: () => viewRender(<Home />) },
    { path: '/posts', action: () => viewRender(<PostList />), canActivate: [MyGuard], canDeactivate: [MyGuard] }
];

Register a component with "setActiveComponent" function in order to access with the Guard

export class PostList extends React.Component<any, any> {
    constructor(props) {
        super(props);
        setActiveComponent('PostList', this);
    }
    checkCanDeactivate() {
        return confirm('Deactivate?');
    }
    render() {
        return (
            <h1>Post list</h1>
        );
    }
}