@trevorhanus/reactx-router
    TypeScript icon, indicating that this package has built-in type declarations

    1.0.6 • Public • Published

    Reactx-Router

    A react router that works well with mobx.

    Overview

    paraphrased from Angular's docs

    Traditional web applications utilize the browser's familiar navigation model:

    • Enter a url in the address bar and the browser loads that page.
    • Click on links on the page and the browser navigates the url to that page and loads it.
    • Click the back and forward button and the browser navigates to the pages in your history.

    A SPA (Single Page Application) takes a different approach. SPA's use JavaScript to replace the contents of a page base on the current route. This makes the website feel much snappier since the browser does not need to load a new page on every redirect.

    Reactx-router is a client-side router that works well with mobx.

    Installing

    $ yarn add @trevorhanus/reactx-router

    Basic Usage

    import { App, Profile } from './components';
    import { MyStore } from './MyStore';
    import { IViewState, Route, router } from '@trevorhanus/reactx-router';
      
    const appRoute = new Route({
        name: 'app',
        path: '/',
        component: App,
        beforeEnter: (state: IViewState, store: MyStore) => {
            if (!store.user.loggedIn) {
                router.goTo('login');
                return false;
            }
        },
        children: [
            new Route({
                name: 'profile',
                path: '/profile',
                component: Profile
            })
        ]
    });
      
    const store = new MyStore();
    router.start<MyStore>([appRoute], store);
      
    reactDOM.render(<Router />, document.getElementById('app'));

    Route

    You create a new Route for every different view or page in your app. Each route must have a unqiue name and can have the following options.

    required

    name: string

    the name of the route. Must be unique for all routes. The name is used in the router.goTo(routeName) method to change the current route.

    path: string

    path at which this route should be activated. These paths can contain path parameters like: /profile/:profileId. Now, when the browser is pointed at www.yourdomain.com/profile/1234 this route would be activated with the path parameters of { profileId: 1234 }

    component: React.Component | React.StatelessComponent

    the react component to render when this route is active.

    optional

    acceptedQueryParams: string[]

    a list of query parameter keys that this route accepts. When this option is set, any query params not in the list will be ignored. Anything is accepted when it is left undefined.

    children: Route[]

    a list of child Routes. See Nested Routes for more info.

    beforeEnter: (viewState: IViewState, store: Store) => boolean | void

    a lifecycle callback which is invoked just before the router changes to this route. Returning false will stop the transition. viewState will be the current viewState, or the viewState before entering this route

    onEnter: (viewState: IViewState, store: Store) => boolean | void

    a lifecycle callback which is invoked just after the router changes to this route. Returning false from this callback will not do anything special, since the router has already transitioned.

    beforeExit: (viewState: IViewState, store: Store) => boolean | void

    a lifecycle callback which is invoked just before the router changes from this route. Returning false will stop the transition.

    Router

    The Router class is responsible for managing the list of routes and the current state. It has the following public API.

    start<Store>(routes: Route[], store: Store, notFoundComponent?: React.Component) => void

    Method that must be called before react renders the <Router /> component. We pass in our list of route trees so the router can be configured. We also pass in our store here, so the router can pass it to our lifecycle callbacks. There is an optional notFoundComponent argument. This is the component that will be rendered when the browser is pointed at an invalid path. Reactx-router has a default component, but it isn't pretty.

    currentParams: IPathParams

    The current path params. This is an observable property.

    // consider the router was started with a route with path `/cities/:cityId/heros/:heroId/` 
    // and the browser was pointed at www.domain.com/cities/new-york-city/heros/iron-man
      
    import { router } from '@trevorhanus/reactx-router';
      
    const currentPathParams = router.currentParams;
      
    console.log(currentPathParams); // { cityId: 'new-york-city', heroId: 'iron-man' }

    currentPath: string

    Current path. This is an observable property.

    currentViewState: IViewState

    Current view state, also observable.

    goTo(name: string, params?: IPathParams, query?: IQueryParams) => void

    Method that transitions the current route to the given route. This method can be called from anywhere in the code base.

    hasRoute(name: string): boolean

    simple utility method.

    Global Singleton

    Reactx-router provides a global singleton instance of the Router class. This singleton makes it easy to access the router from an module (or file) in your app. You simply add the import { router } from 'reactx-router'; statement in any file and you can access the current properties or invoke the Router#goTo method.

    If for some reason this does not suit you, you can import the Router class and create your own instance like so.

    import { Router, ReactxRouter } from 'reactx-router';
      
    const myRouter = new Router();
      
    myRouter.start<MyStore>(myRoutes, myStore);
      
    reactDOM.render(<ReactxRouter router={myRouter} />, domNode);

    Lifecycle Callbacks

    Every route can be passed three optional lifecycle callbacks: beforeEnter, onEnter, and beforeExit. The callbacks are each passed two arguments, viewState and store. The viewState (see IViewState) includes data about the current route: path, params, queryParams, and route. The store is whatever object was passed into the router#start method. These callbacks are invoked when the route is transitioned and allow us to do any setup or teardown that we need to do in our store. For instance, we could fire off a fetch data action or check to make sure the user is logged in.

    If either of the beforeEnter or beforeExit callbacks return false the route transition will be terminated. You can also call router.goTo(<routeName>) inside a callback to transition to another route. Warning: you still must return false after this call or the route will not transition.

    Nested Routes

    Nesting routes allows you to compose your UI out of reusable components. It also allows you to protect a certain subset of routes. Consider the following route tree.

    const appRoute = new Route({
        name: 'app',
        path: '/',
        component: App,
        beforeEnter: (state: IViewState, store: MyStore) => {
            if (!store.user.loggedIn) {
                router.goTo('login');
                return false;
            }
        },
        children: [
            new Route({
                name: 'welcome',
                path: '/',
                component: Welcome
            }),
            new Route({
                name: 'profile',
                path: '/profile',
                component: Profile
            }),
        ],
    });

    Now take a look at these components

    App.tsx

    export interface IAppProps {
        routerOutlet?: any;    
    }
     
    const App = observer((props: IAppProps) => {
        return (
            <AppHeader />
            <AppNav />
            { props.routerOutlet }
            <AppFooter />
        );
    });

    Welcome.tsx

    const Welcome = observer((props: {}) => {
        return (
            <h1>Welcome to our site!</h1>
        );
    });

    Profile.tsx

    const Profile = observer((props: {}) => {
        return (
            <h1>This is the Profile section</h1>
        );
    });

    When the browser is pointed at / then both the <App /> component and the <Welcome /> component will be rendered. Since the welcome route is nested under the app route, the <Welcome /> component will be added as the props.routerOutlet prop on the App component. Now, the developer can decide where in that component to render it.

    When the browser is pointed at /profile, both the <App /> component and the <Profile /> component will be rendered.

    It's also important to note that in each of the cases above, the beforeEnter callback of the app route will be invoked. In this case, since the beforeEnter callback is checking to make sure the user is logged in, both the welcome and profile routes are being protected.

    Running the Tests

    make sure you have everything installed

    $ yarn install

    run the tests

    $ yarn test

    Keywords

    none

    Install

    npm i @trevorhanus/reactx-router

    DownloadsWeekly Downloads

    26

    Version

    1.0.6

    License

    ISC

    Unpacked Size

    56.8 kB

    Total Files

    37

    Last publish

    Collaborators

    • trevorhanus