Last active
November 4, 2018 15:49
-
-
Save acdlite/26628a2b3b7bdc76f805 to your computer and use it in GitHub Desktop.
Minimal router using existing React Router primitives
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const history = createHistory(); | |
const router = createReactRouter(routes); | |
history.listen((location => { | |
router.match(location, (err, state, redirectInfo) => { | |
// Integrate with external state library (Redux), or render directly | |
React.render( | |
<RouterComponent {...state} />; | |
); | |
}); | |
}); | |
// On the server, just omit history | |
router.match(createLocation(path), (err, state, redirectInfo) => { | |
React.renderToString( | |
<RouterComponent {...state} />; | |
); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* type RouterState = { | |
* activeRoutes: Array<Route>, | |
* params: Object | |
* } | |
*/ | |
import matchRoutes from './matchRoutes'; | |
import runTransitionHooks from './runTransitionHooks'; | |
/** | |
* Base createRouter() function with no React-specific functionality, | |
* because #modularity | |
*/ | |
export function createRouter(routes) { | |
let state; | |
function match(location, callback) { | |
matchRoutes(routes, location, (error, nextState) => { | |
if (!error) { | |
state = nextState; | |
} | |
callback(error, nextState); | |
}); | |
} | |
function getState() { | |
return state; | |
} | |
return { | |
match, | |
getState | |
}; | |
} | |
/** | |
* Router enhancer runs transition hooks when matching | |
*/ | |
export function useTransitionHooks(next) { | |
return routes => { | |
const router = next(routes); | |
function match(location, callback) { | |
const prevState = router.getState(); | |
router.match(location, (error, nextState) => { | |
if (error || nextState == null) { | |
callback(error, null); | |
return; | |
} | |
runTransitionHooks(prevState, nextState, (transitionError, redirectInfo) => { | |
if (error || redirectInfo) { | |
callback(error, null, redirectInfo); | |
} | |
callback(null, nextState); | |
}); | |
}); | |
} | |
return { | |
...router, | |
match | |
}; | |
}; | |
} | |
/** | |
* Router-creating function with transition hook functionality added | |
*/ | |
export const createReactRouter = useTransitionHooks(createRouter); |
Another cool thing is that there's no separate API for server-side rendering: just omit history and pass a location directly to router.match()
.
I really like the approach that you're taking with the react
function, making it work like a piece of middleware. I wonder if we can just pass the createRouter
function directly to it, like you did here with the enableQueries
function. So it would look like:
var router = useTransitionHooks(createRouter)();
@mjackson That's essentially what this line is doing
export const createReactRouter = react(createRouter)
:)
Oh but you're right that useTransitionHooks()
is a better name for that function. Nothing React-y about it.
And yeah I think with an API like this there's potential for a really nice middleware ecosystem, a la Redux.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
New router API proposal and (partial) implementation. (Missing piece is
<RouterComponent>
which renders for a given router state.)This was surprisingly easy to whip together — all the hard work was already done with
matchRoutes()
andrunTransitionHooks()
. Awesome job, React Router team!Key points:
location
(handled by history) andactiveRoutes
.