Other projects:
- 🏫 React Academy - Interactive React and GraphQL workshops
- 💌 Twizzy - A standalone app for Twitter DM
- 💻 Sizzy - A tool for testing responsive design on multiple devices at once
- 🤖 JSUI - A powerful UI toolkit for managing JavaScript apps
〽️ MobX Router
Example usage
Inspiration
📖 How to decouple state and UI - a.k.a. you don’t need componentWillMount
Features
- Decoupled state from UI
- Central route configuration
- URL changes are triggering changes directly in the store, and vice-versa
- No need to use component lifecycle methods like
componentWillMount
to fetch data or trigger a side effect in the store - Supported callbacks for the routes are:
beforeEnter
, onEnter
, beforeExit
, onExit
. All of the callbacks receive route
, params
, store
, and queryParams
as parameters. If the beforeExit
or beforeEnter
methods return false
the navigation action will be prevented. - The current URL params and query params are accessible directly in the store
store.router.params
/ store.router.queryParams
so basically they're available everywhere without any additional wrapping or HOC. - Navigating to another route happens by calling the
goTo
method on the router store, and the changes in the url are reflected automatically. So for example you can call router.goTo(routes.book, {id:5, page:3})
and after the change is made in the store, the URL change will follow. You never directly manipulate the URL or the history object. <Link>
component which also populates the href attribute and works with middle click
or cmd/ctrl + click
- Typescript support (Converted to typescript by thdk)
- Hash-based routing (using paths like
/#/foo/bar
) support
Implementation
import React, {createContext} from 'react';
import ReactDOM from 'react-dom';
import {MobxRouter, RouterStore, startRouter} from 'mobx-router';
import routes from 'config/routes';
export class AppStore {
title = 'MobX Router Example App',
user = null
}
export class RootStore {
public router: RouterStore<RootStore>;
public app: AppStore;
constructor() {
this.router = new RouterStore<RootStore>(this);
this.app = new AppStore();
}
}
const store = new RootStore();
const StoreContext = createContext({});
const StoreProvider = StoreContext.Provider;
startRouter(routes, store);
ReactDOM.render(
<StoreProvider value={store}>
<MobxRouter store={store}/>
</StoreProvider>, document.getElementById('root')
)
Example config
/config/routes.js
import React from 'react';
import {Route} from 'mobx-router';
import Home from 'components/Home';
import Document from 'components/Document';
import Gallery from 'components/Gallery';
import Book from 'components/Book';
import UserProfile from 'components/UserProfile';
const routes = {
home: new Route({
path: '/',
component: <Home/>
}),
userProfile: new Route({
path: '/profile/:username/:tab',
component: <UserProfile/>,
onEnter: () => {
console.log('entering user profile!');
},
beforeExit: () => {
console.log('exiting user profile!');
},
onParamsChange: (route, params, store) => {
console.log('params changed to', params);
}
}),
gallery: new Route({
path: '/gallery',
component: <Gallery/>,
onEnter: (route, params, store, queryParams) => {
store.gallery.fetchImages();
console.log('current query params are -> ', queryParams);
},
beforeExit: () => {
const result = confirm('Are you sure you want to leave the gallery?');
return result;
}
}),
document: new Route({
path: '/document/:id',
component: <Document/>,
beforeEnter: (route, params, store) => {
const userIsLoggedIn = store.app.user;
if (!userIsLoggedIn) {
alert('Only logged in users can enter this route!');
return false;
}
},
onEnter: (route, params) => {
console.log(`entering document with params`, params);
}
}),
book: new Route({
path: '/book/:id/page/:page',
component: <Book/>,
onEnter: (route, params, store) => {
console.log(`entering book with params`, params);
store.app.setTitle(route.title);
}
})
};
export default routes;
Custom director configuration
mobx-router uses director behind the scenes. mobx-router exposes the director config object for you to pass your own configuration to director.
To do this you must pass a DirectorConfig
object as third argument of startRouter
method.
Hash based Routing | html5history
If you disable html5history option, mobx will fallback to hash based routing.
startRouter(
routes,
store,
{
html5history: false,
}
);
Not found (404) route | notfound
You can pass a function to notfound
which will be called when you don't have any matching route for the current path.
startRouter(
routes,
store,
{
notfound: () => store.router.goTo(YOUR_NOT_FOUND_ROUTE),
}
);