Skip to content

Commit 6e4ed23

Browse files
committed
Fix isLoading for multiple async calls
1 parent e45c615 commit 6e4ed23

File tree

2 files changed

+99
-37
lines changed

2 files changed

+99
-37
lines changed

src/alt/store/StoreMixin.js

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,39 +22,45 @@ const StoreMixin = {
2222
},
2323

2424
exportAsync(asyncMethods) {
25-
let isLoading = false
26-
let hasError = false
25+
this.registerAsync(asyncMethods)
26+
},
27+
28+
registerAsync(asyncDef) {
29+
let loadCounter = 0
30+
31+
const asyncMethods = fn.isFunction(asyncDef)
32+
? asyncDef(this.alt)
33+
: asyncDef
2734

2835
const toExport = Object.keys(asyncMethods).reduce((publicMethods, methodName) => {
29-
const asyncSpec = asyncMethods[methodName](this)
36+
const desc = asyncMethods[methodName]
37+
const spec = fn.isFunction(desc) ? desc(this) : desc
3038

3139
const validHandlers = ['success', 'error', 'loading']
3240
validHandlers.forEach((handler) => {
33-
if (asyncSpec[handler] && !asyncSpec[handler][Sym.ACTION_KEY]) {
41+
if (spec[handler] && !spec[handler][Sym.ACTION_KEY]) {
3442
throw new Error(`${handler} handler must be an action function`)
3543
}
3644
})
3745

3846
publicMethods[methodName] = (...args) => {
3947
const state = this.getInstance().getState()
40-
const value = asyncSpec.local && asyncSpec.local(state, ...args)
41-
const shouldFetch = asyncSpec.shouldFetch ? asyncSpec.shouldFetch(state, ...args) : !value
48+
const value = spec.local && spec.local(state, ...args)
49+
const shouldFetch = spec.shouldFetch ? spec.shouldFetch(state, ...args) : !value
4250

4351
// if we don't have it in cache then fetch it
4452
if (shouldFetch) {
45-
isLoading = true
46-
hasError = false
53+
loadCounter += 1
4754
/* istanbul ignore else */
48-
if (asyncSpec.loading) asyncSpec.loading()
49-
asyncSpec.remote(state, ...args)
55+
if (spec.loading) spec.loading()
56+
spec.remote(state, ...args)
5057
.then((v) => {
51-
isLoading = false
52-
asyncSpec.success(v)
58+
loadCounter -= 1
59+
spec.success(v)
5360
})
5461
.catch((v) => {
55-
isLoading = false
56-
hasError = true
57-
asyncSpec.error(v)
62+
loadCounter -= 1
63+
spec.error(v)
5864
})
5965
} else {
6066
// otherwise emit the change now
@@ -67,8 +73,7 @@ const StoreMixin = {
6773

6874
this.exportPublicMethods(toExport)
6975
this.exportPublicMethods({
70-
isLoading: () => isLoading,
71-
hasError: () => hasError
76+
isLoading: () => loadCounter > 0
7277
})
7378
},
7479

test/async-test.js

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,33 @@ const StargazerSource = {
3939
error: StargazerActions.failed
4040
}
4141
},
42-
alwaysFetchUsers() {
43-
return {
44-
remote,
45-
local: () => true,
46-
loading: StargazerActions.fetchingUsers,
47-
success: StargazerActions.usersReceived,
48-
error: StargazerActions.failed,
49-
shouldFetch: () => true
50-
}
42+
43+
alwaysFetchUsers: {
44+
remote,
45+
local: () => true,
46+
loading: StargazerActions.fetchingUsers,
47+
success: StargazerActions.usersReceived,
48+
error: StargazerActions.failed,
49+
shouldFetch: () => true
5150
},
52-
neverFetchUsers() {
53-
return {
54-
remote,
55-
local: () => false,
56-
loading: StargazerActions.fetchingUsers,
57-
success: StargazerActions.usersReceived,
58-
error: StargazerActions.failed,
59-
shouldFetch: () => false
60-
}
51+
52+
neverFetchUsers: {
53+
remote,
54+
local: () => false,
55+
loading: StargazerActions.fetchingUsers,
56+
success: StargazerActions.usersReceived,
57+
error: StargazerActions.failed,
58+
shouldFetch: () => false
59+
},
60+
61+
fetchRepos: {
62+
remote() {
63+
return new Promise((resolve, reject) => {
64+
setTimeout(() => resolve('TESTTEST'), 200)
65+
})
66+
},
67+
success: StargazerActions.usersReceived,
68+
error: StargazerActions.failed
6169
}
6270
}
6371

@@ -165,7 +173,6 @@ export default {
165173
count()
166174
test()
167175
assert.ok(local.calledOnce)
168-
assert.notOk(StargazerStore.hasError(), 'no errors')
169176
assert.notOk(StargazerStore.isLoading())
170177
assert(remote.callCount === 0)
171178
done()
@@ -185,7 +192,6 @@ export default {
185192
assert(state.users.length === 0)
186193
} else if (spy.callCount === 2) {
187194
assert.match(state.errorMessage, /things broke/)
188-
assert.ok(StargazerStore.hasError(), 'we have an error')
189195
count()
190196
test()
191197
assert.notOk(StargazerStore.isLoading())
@@ -210,5 +216,56 @@ export default {
210216
assert.notOk(StargazerStore.isLoading())
211217
assert(remote.callCount === 0)
212218
},
219+
220+
'multiple loads'(done) {
221+
const unsub = StargazerStore.listen((state) => {
222+
if (state.users === 'TESTTEST') {
223+
assert.notOk(StargazerStore.isLoading())
224+
unsub()
225+
done()
226+
} else {
227+
assert.ok(StargazerStore.isLoading())
228+
}
229+
})
230+
231+
StargazerStore.fetchUsers()
232+
StargazerStore.fetchRepos()
233+
assert.ok(StargazerStore.isLoading())
234+
},
235+
236+
'as a function'() {
237+
const FauxSource = sinon.stub().returns({})
238+
239+
@datasource(FauxSource)
240+
class FauxStore {
241+
static displayName = 'FauxStore'
242+
}
243+
244+
const store = alt.createStore(FauxStore)
245+
246+
assert(FauxSource.firstCall.args[0] === alt)
247+
assert.isFunction(store.isLoading)
248+
},
249+
250+
'as an object'() {
251+
const actions = alt.generateActions('test')
252+
253+
const PojoSource = {
254+
justTesting: {
255+
success: actions.test,
256+
error: actions.test,
257+
}
258+
}
259+
260+
@datasource(PojoSource)
261+
class MyStore {
262+
static displayName = 'MyStore'
263+
}
264+
265+
const store = alt.createStore(MyStore)
266+
267+
assert.isFunction(store.justTesting)
268+
assert.isFunction(store.isLoading)
269+
},
213270
}
214271
}

0 commit comments

Comments
 (0)