Skip to content

Instantly share code, notes, and snippets.

@ArthurClemens
Last active July 23, 2017 14:17
Show Gist options
  • Save ArthurClemens/327dda481b9fd227ee86 to your computer and use it in GitHub Desktop.
Save ArthurClemens/327dda481b9fd227ee86 to your computer and use it in GitHub Desktop.
Mithril routing with Redux. Fiddle: https://jsfiddle.net/ArthurClemens/dzqv0s4q/
// app/redux/action-creators.es6.js
import {SET_ROUTE} from 'app/redux/action-types';
export const setRoute = (route) => {
return {
type: SET_ROUTE,
route
};
};
// app/redux/action-types.es6.js
export const SET_ROUTE = 'SET_ROUTE';
// app/index/index.es6.js
import router from 'app/redux/router';
import m from 'mithril';
import {store, observeStore} from 'app/redux/store';
const index = {};
index.view = () => {
return m('div', [
m('h1', [
'path: ',
m.route()
]),
m('ul', [
m('li', m('a', {
href: '/home',
config: m.route
}, 'Home')),
m('li', m('a', {
href: '/favorites',
config: m.route
}, 'Favorites')),
m('li', m('a', {
href: '/settings',
config: m.route
}, 'Settings'))
])
]);
};
m.route.mode = 'hash';
const routes = {
'/': router(m.component(index)),
'/home': router(m.component(index)),
'/favorites': router(m.component(index)),
'/settings': router(m.component(index))
};
// To debug changes
observeStore(store, (state) => state.route.path, (path) => {
console.log("changed path", path);
});
m.route(document.body, '/', routes);
// app/redux/reducers.es6.js
import {combineReducers} from 'redux';
import {SET_ROUTE} from 'app/redux/action-types';
const route = (state = {path: undefined}, action) => {
switch (action.type) {
case SET_ROUTE:
return action.route;
default:
return state;
}
};
const app = combineReducers({
route
// assuming more reducers are added here
});
export default app;
// app/redux/router.es6.js
import m from 'mithril';
import {store, observeStore} from 'app/redux/store';
import {setRoute} from 'app/redux/action-creators';
window.addEventListener((m.route.mode === 'hash') ? 'hashchange' : 'popstate', () => passiveRouteChange(window.location));
observeStore(store, (state) => state.route, (route) => {
if (!route.path) {
return;
}
// Redirect with m.route() unless explicitly stated
// Calls from passiveDispatch do not redirect.
const redirect = (route.redirect === undefined || route.redirect);
if (redirect) {
m.route(route.path);
}
});
const passiveDispatch = (path, redirect = false) => (
store.dispatch(setRoute({
path,
redirect
}))
);
const passiveRouteChange = (location) => {
const path = m.route.mode === 'hash'
? location.hash.substring(1)
: m.route.mode === 'search'
? location.search.substring(1)
: location.pathname;
if (path) {
passiveDispatch(path);
}
};
/*
m.route wrapper
Call as:
const routes = {
'/home': router(m.component(home))
};
*/
export default function(component) {
return {
controller: function() {
const currentPath = store.getState().route.path;
const newPath = m.route();
if (newPath !== currentPath) {
passiveDispatch(newPath);
}
return new component.controller();
},
view: component.view
};
};
import {createStore} from 'redux';
import app from 'app/redux/reducers';
export const store = createStore(app, {});
export const observeStore = (store, select, onChange) => {
let currentState;
const handleChange = () => {
let nextState = select(store.getState());
if (nextState !== currentState) {
currentState = nextState;
onChange(currentState);
}
};
const unsubscribe = store.subscribe(handleChange);
handleChange();
return unsubscribe;
};
@ArthurClemens
Copy link
Author

Assuming a setup of:

  • app
    • index
      • index.es6.js
    • redux
      • action-types.es6.js
      • action-creators.es6.js
      • reducers.es6.js
      • store.es6.js
      • router.es6.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment