Skip to content

Commit 2f9b25f

Browse files
committed
Init stores with state
1 parent 0e7bd85 commit 2f9b25f

File tree

2 files changed

+57
-21
lines changed

2 files changed

+57
-21
lines changed

index.js

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function Choo (opts) {
4242
this._hasWindow = typeof window !== 'undefined'
4343
this._cache = opts.cache
4444
this._loaded = false
45-
this._stores = []
45+
this._stores = [ondomtitlechange]
4646
this._tree = null
4747

4848
// state
@@ -66,11 +66,13 @@ function Choo (opts) {
6666

6767
// listen for title changes; available even when calling .toString()
6868
if (this._hasWindow) this.state.title = document.title
69-
this.emitter.prependListener(this._events.DOMTITLECHANGE, function (title) {
70-
assert.equal(typeof title, 'string', 'events.DOMTitleChange: title should be type string')
71-
self.state.title = title
72-
if (self._hasWindow) document.title = title
73-
})
69+
function ondomtitlechange (state) {
70+
self.emitter.prependListener(self._events.DOMTITLECHANGE, function (title) {
71+
assert.equal(typeof title, 'string', 'events.DOMTitleChange: title should be type string')
72+
state.title = title
73+
if (self._hasWindow) document.title = title
74+
})
75+
}
7476
}
7577

7678
Choo.prototype.route = function (route, handler) {
@@ -97,7 +99,7 @@ Choo.prototype.start = function () {
9799
var self = this
98100
if (this._historyEnabled) {
99101
this.emitter.prependListener(this._events.NAVIGATE, function () {
100-
self._matchRoute()
102+
self._matchRoute(self.state)
101103
if (self._loaded) {
102104
self.emitter.emit(self._events.RENDER)
103105
setTimeout(scrollToAnchor.bind(null, window.location.hash), 0)
@@ -142,7 +144,7 @@ Choo.prototype.start = function () {
142144
initStore(self.state)
143145
})
144146

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

@@ -204,26 +206,27 @@ Choo.prototype.mount = function mount (selector) {
204206
}
205207

206208
Choo.prototype.toString = function (location, state) {
207-
this.state = xtend(this.state, state || {})
209+
state = state || {}
210+
state.events = xtend(this._events)
208211

209212
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')
210213
assert.equal(typeof location, 'string', 'choo.toString: location should be type string')
211-
assert.equal(typeof this.state, 'object', 'choo.toString: state should be type object')
214+
assert.equal(typeof state, 'object', 'choo.toString: state should be type object')
212215

213-
var self = this
214-
this._setCache(this.state)
216+
this._setCache(state)
217+
this.emitter.removeAllListeners()
215218
this._stores.forEach(function (initStore) {
216-
initStore(self.state)
219+
initStore(state)
217220
})
218221

219-
this._matchRoute(location)
220-
var html = this._prerender(this.state)
222+
this._matchRoute(state, location)
223+
var html = this._prerender(state)
221224
assert.ok(html, 'choo.toString: no valid value returned for the route ' + location)
222225
assert(!Array.isArray(html), 'choo.toString: return value was an array for the route ' + location)
223226
return typeof html.outerHTML === 'string' ? html.outerHTML : html.toString()
224227
}
225228

226-
Choo.prototype._matchRoute = function (locationOverride) {
229+
Choo.prototype._matchRoute = function (state, locationOverride) {
227230
var location, queryString
228231
if (locationOverride) {
229232
location = locationOverride.replace(/\?.+$/, '').replace(/\/$/, '')
@@ -236,11 +239,10 @@ Choo.prototype._matchRoute = function (locationOverride) {
236239
}
237240
var matched = this.router.match(location)
238241
this._handler = matched.cb
239-
this.state.href = location
240-
this.state.query = nanoquery(queryString)
241-
this.state.route = matched.route
242-
this.state.params = matched.params
243-
return this.state
242+
state.href = location
243+
state.query = nanoquery(queryString)
244+
state.route = matched.route
245+
state.params = matched.params
244246
}
245247

246248
Choo.prototype._prerender = function (state) {

test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,37 @@ tape('state should include cache', function (t) {
228228
t.equal(arg, 'arg', 'constructor args were forwarded')
229229
}
230230
})
231+
232+
tape('state should not leak on toString', function (t) {
233+
t.plan(6)
234+
235+
var app = choo()
236+
app.use(store)
237+
238+
var routes = ['foo', 'bar']
239+
var states = routes.map(function (route) {
240+
var state = {}
241+
app.route(`/${route}`, view)
242+
app.toString(`/${route}`, state)
243+
return state
244+
})
245+
246+
for (var i = 0, len = routes.length; i < len; i++) {
247+
t.equal(states[i].test, routes[i], 'store was used')
248+
t.equal(states[i].title, routes[i], 'title was added to state')
249+
}
250+
251+
function store (state, emitter) {
252+
state.test = null
253+
emitter.on('test', function (str) {
254+
t.equal(state.test, null, 'state has been reset')
255+
state.test = str
256+
})
257+
}
258+
259+
function view (state, emit) {
260+
emit('test', state.route)
261+
emit(state.events.DOMTITLECHANGE, state.route)
262+
return html`<body>Hello ${state.route}</body>`
263+
}
264+
})

0 commit comments

Comments
 (0)