Skip to content

Instantly share code, notes, and snippets.

@searls
Last active June 17, 2018 16:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save searls/972536bd9479d40461b200d4766814c4 to your computer and use it in GitHub Desktop.
Save searls/972536bd9479d40461b200d4766814c4 to your computer and use it in GitHub Desktop.
override preact-router so we can synchronously change the route & trigger a render
/*
* override preact-router so we can synchronously change the route & trigger a render
* reason for doing this: often a UI action triggers an XHR which in turn will determine
* where the user needs to go next (it's a flashcard game, so whether they got it right or wrong
* will determine whether their session has concluded, whether to draw the next card, and so on)
* as a result we have a lot of action logic that wants to in one-fell-swoop programmatically route
* the user to a different component and also update the properties passed to the components by
* re-rendering (one big top-level render function, all state just passed by props, no stateful components)
*
* The practical effect of this change versus the built-in router was that it cuts the number of
* calls to render in half when a single event triggers both a route & props change. Particularly
* in really slow-painting browsers (actually just Chrome, LOL), users would see a flash of un-updated content.
*
* Below are just relevant snippets from a larger app and not a runnable example
*/
// Custom router
class RenderWhileYouRouter extends Router {
componentWillReceiveProps(nextProps) {
if (nextProps.url && nextProps.url !== this.props.url) {
route(nextProps.url)
}
}
}
// routing function
app.route.andRerender = (path, props) => {
app.render(_.extend({}, props, {url: path}))
}
// render function
app.render = (props) => {
render(
h(app.view.main, props),
document.body,
document.body.lastElementChild
)
}
// top-level view
app.view.main = (props) => {
return h('div', {id: 'app', class: 'kamesame'},
app.view.nav(props),
h(RenderWhileYouRouter, _.extend({id: 'content'}, props),
app.view.route('', app.view.home, props),
app.view.route('login', app.view.login, props),
app.view.route('lessons', app.view.lessons, props),
// ………
app.view.route('account', app.view.account, props),
h(app.view.notFound, {default: true})
),
app.view.footer()
)
}
// Example action:
app.study.handleAnswer = (meaning, node, mode) => {
const answer = _.trim(node.value)
app.ws.request({id: meaning.id, answer, mode}, (res) => {
const props = app.prop.merge(res)
app.store.save(props)
app.route.andRerender(`/app/${mode}/${meaning.id}/result`, props)
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment