Skip to content

Commit

Permalink
Init stores with state
Browse files Browse the repository at this point in the history
  • Loading branch information
tornqvist committed Mar 22, 2018
1 parent 357d508 commit 44d3b9c
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 23 deletions.
48 changes: 25 additions & 23 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function Choo (opts) {
this._hasWindow = typeof window !== 'undefined'
this._createLocation = nanolocation
this._loaded = false
this._stores = []
this._stores = [ondomtitlechange]
this._tree = null

// properties that are part of the API
Expand All @@ -60,11 +60,13 @@ function Choo (opts) {

// listen for title changes; available even when calling .toString()
if (this._hasWindow) this.state.title = document.title
this.emitter.prependListener(this._events.DOMTITLECHANGE, function (title) {
assert.equal(typeof title, 'string', 'events.DOMTitleChange: title should be type string')
self.state.title = title
if (self._hasWindow) document.title = title
})
function ondomtitlechange (state) {
self.emitter.prependListener(self._events.DOMTITLECHANGE, function (title) {
assert.equal(typeof title, 'string', 'events.DOMTitleChange: title should be type string')
state.title = title
if (self._hasWindow) document.title = title
})
}
}

Choo.prototype.route = function (route, handler) {
Expand All @@ -76,11 +78,11 @@ Choo.prototype.route = function (route, handler) {
Choo.prototype.use = function (cb) {
assert.equal(typeof cb, 'function', 'choo.use: cb should be type function')
var self = this
this._stores.push(function () {
this._stores.push(function (state) {
var msg = 'choo.use'
msg = cb.storeName ? msg + '(' + cb.storeName + ')' : msg
var endTiming = nanotiming(msg)
cb(self.state, self.emitter, self)
cb(state, self.emitter, self)
endTiming()
})
}
Expand All @@ -91,7 +93,7 @@ Choo.prototype.start = function () {
var self = this
if (this._historyEnabled) {
this.emitter.prependListener(this._events.NAVIGATE, function () {
self._matchRoute()
self._matchRoute(self.state)
if (self._loaded) {
self.emitter.emit(self._events.RENDER)
setTimeout(scrollToAnchor.bind(null, window.location.hash), 0)
Expand Down Expand Up @@ -129,10 +131,10 @@ Choo.prototype.start = function () {
}

this._stores.forEach(function (initStore) {
initStore()
initStore(self.state)
})

this._matchRoute()
this._matchRoute(this.state)
this._tree = this._prerender(this.state)
assert.ok(this._tree, 'choo.start: no valid DOM node returned for location ' + this.state.href)

Expand Down Expand Up @@ -194,25 +196,26 @@ Choo.prototype.mount = function mount (selector) {
}

Choo.prototype.toString = function (location, state) {
this.state = xtend(this.state, state || {})
state = state || {}
state.events = xtend(this._events)

assert.notEqual(typeof window, 'object', 'choo.mount: window was found. .toString() must be called in Node, use .start() or .mount() if running in the browser')
assert.equal(typeof location, 'string', 'choo.toString: location should be type string')
assert.equal(typeof this.state, 'object', 'choo.toString: state should be type object')
assert.equal(typeof state, 'object', 'choo.toString: state should be type object')

// TODO: pass custom state down to each store.
this.emitter.removeAllListeners()
this._stores.forEach(function (initStore) {
initStore()
initStore(state)
})

this._matchRoute(location)
var html = this._prerender(this.state)
this._matchRoute(state, location)
var html = this._prerender(state)
assert.ok(html, 'choo.toString: no valid value returned for the route ' + location)
assert(!Array.isArray(html), 'choo.toString: return value was an array for the route ' + location)
return typeof html.outerHTML === 'string' ? html.outerHTML : html.toString()
}

Choo.prototype._matchRoute = function (locationOverride) {
Choo.prototype._matchRoute = function (state, locationOverride) {
var location, queryString
if (locationOverride) {
location = locationOverride.replace(/\?.+$/, '')
Expand All @@ -223,11 +226,10 @@ Choo.prototype._matchRoute = function (locationOverride) {
}
var matched = this.router.match(location)
this._handler = matched.cb
this.state.href = location
this.state.query = nanoquery(queryString)
this.state.route = matched.route
this.state.params = matched.params
return this.state
state.href = location
state.query = nanoquery(queryString)
state.route = matched.route
state.params = matched.params
}

Choo.prototype._prerender = function (state) {
Expand Down
34 changes: 34 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,37 @@ tape('should render on the server with hyperscript', function (t) {
t.equal(res.toString().trim(), exp, 'result was OK')
t.end()
})

tape('should not leak state on render', function (t) {
t.plan(6)

var app = choo()
app.use(store)

var routes = ['foo', 'bar']
var states = routes.map(function (route) {
var state = {}
app.route(`/${route}`, view)
app.toString(`/${route}`, state)
return state
})

for (var i = 0, len = routes.length; i < len; i++) {
t.equal(states[i].test, routes[i], 'store was used')
t.equal(states[i].title, routes[i], 'title was added to state')
}

function store (state, emitter) {
state.test = null
emitter.on('test', function (str) {
t.equal(state.test, null, 'state has been reset')
state.test = str
})
}

function view (state, emit) {
emit('test', state.route)
emit(state.events.DOMTITLECHANGE, state.route)
return html`<body>Hello ${state.route}</body>`
}
})

0 comments on commit 44d3b9c

Please sign in to comment.