Skip to content

Commit 2ea0787

Browse files
fix(overmind): fix async pipes, cache scoped tree in dev and proxy all effects
1 parent 3a1edc6 commit 2ea0787

File tree

2 files changed

+79
-70
lines changed

2 files changed

+79
-70
lines changed

packages/node_modules/overmind/src/index.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,6 @@ export class Overmind<Config extends Configuration> implements Configuration {
118118

119119
this.initializeReactions(configuration, eventHub, proxyStateTree)
120120

121-
/*
122-
Identify when the state tree should flush out changes
123-
124-
TODO:
125-
- How should I know when an operator is connected to a flushId?
126-
- Pass in operatorId on :async and set flushId directly?
127-
- Store operatorId on the flush and check?
128-
*/
129121
eventHub.on(EventType.OPERATOR_ASYNC, (data) => {
130122
const flushData = proxyStateTree.flush(true)
131123
if (this.devtools && flushData.mutations.length) {
@@ -255,7 +247,9 @@ export class Overmind<Config extends Configuration> implements Configuration {
255247
}
256248
this.eventHub.emit(EventType.ACTION_START, execution)
257249
this.eventHub.emit(EventType.OPERATOR_START, execution)
258-
const scopedTree = this.proxyStateTree.getScopedTree()
250+
251+
const scopedTree = (action.__scopedTree =
252+
action.__scopedTree || this.proxyStateTree.getScopedTree())
259253
scopedTree.startMutationTracking()
260254
const result = action(
261255
this.createContext(
@@ -513,10 +507,28 @@ export function pipe(...operators) {
513507
if (err) next(err)
514508
else {
515509
let operatorIndex = 0
516-
const run = (runErr, runContext) =>
517-
operators[operatorIndex++](runErr, runContext, runNextOperator, final)
510+
let asyncTimeout
511+
const finalClearingAsync = (...args) => {
512+
clearTimeout(asyncTimeout)
513+
final(...args)
514+
}
515+
const run = (runErr, runContext) => {
516+
asyncTimeout = setTimeout(() => {
517+
context.execution.emit(EventType.OPERATOR_ASYNC, {
518+
...context.execution,
519+
isAsync: true,
520+
})
521+
})
522+
operators[operatorIndex++](
523+
runErr,
524+
runContext,
525+
runNextOperator,
526+
finalClearingAsync
527+
)
528+
}
518529

519530
const runNextOperator = (operatorError, operatorContext) => {
531+
clearTimeout(asyncTimeout)
520532
if (operatorError) return next(operatorError)
521533
if (operatorIndex >= operators.length)
522534
return next(null, operatorContext)

packages/node_modules/overmind/src/proxyfyEffects.ts

Lines changed: 56 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,60 +6,68 @@ function isObject(value) {
66

77
let hasWarnedConstructor = false
88

9+
function wrapEffect(target, prop, path, cb) {
10+
const func = function(...args) {
11+
const result = target[prop].apply(this, args)
12+
if (result instanceof Promise) {
13+
result.then((promisedResult) => {
14+
// eslint-disable-next-line standard/no-callback-literal
15+
cb({
16+
name: path,
17+
method: prop,
18+
args,
19+
result: promisedResult,
20+
})
21+
})
22+
} else {
23+
// eslint-disable-next-line standard/no-callback-literal
24+
cb({
25+
name: path,
26+
method: prop,
27+
args,
28+
result,
29+
})
30+
}
31+
return result
32+
}
33+
34+
return new Proxy(func, {
35+
construct(_, args) {
36+
// eslint-disable-next-line
37+
cb({
38+
name: path,
39+
method: prop,
40+
args,
41+
result: `[${prop.toString()}]`,
42+
})
43+
44+
if (!hasWarnedConstructor) {
45+
console.warn(
46+
`EFFECTS - It is highly recommended to create a custom effect, exposing a method that deals with the instantiation of "${path}.${prop.toString()}". It improves readability and debugability of your app`
47+
)
48+
hasWarnedConstructor = true
49+
}
50+
51+
return new target[prop](...args)
52+
},
53+
})
54+
}
55+
956
function createProxyGetHandler(
1057
path: string,
1158
cb: (effect) => void
1259
): ProxyHandler<any>['get'] {
1360
return (target, prop) => {
1461
if (typeof target[prop] === 'function') {
15-
const func = function(...args) {
16-
const result = target[prop].apply(this, args)
17-
if (result instanceof Promise) {
18-
result.then((promisedResult) => {
19-
// eslint-disable-next-line standard/no-callback-literal
20-
cb({
21-
name: path,
22-
method: prop,
23-
args,
24-
result: promisedResult,
25-
})
26-
})
27-
} else {
28-
// eslint-disable-next-line standard/no-callback-literal
29-
cb({
30-
name: path,
31-
method: prop,
32-
args,
33-
result,
34-
})
35-
}
36-
return result
37-
}
38-
39-
return new Proxy(func, {
40-
construct(_, args) {
41-
// eslint-disable-next-line
42-
cb({
43-
name: path,
44-
method: prop,
45-
args,
46-
result: `[${prop.toString()}]`,
47-
})
48-
49-
if (!hasWarnedConstructor) {
50-
console.warn(
51-
`EFFECTS - It is highly recommended to create a custom effect, exposing a method that deals with the instantiation of "${path}.${prop.toString()}". It improves readability and debugability of your app`
52-
)
53-
hasWarnedConstructor = true
54-
}
55-
56-
return new target[prop](...args)
57-
},
58-
})
62+
return wrapEffect(target, prop, path, cb)
5963
}
64+
6065
if (isObject(target[prop])) {
6166
return new Proxy(target[prop], {
62-
get: createProxyGetHandler(path + '.' + prop.toString(), cb),
67+
get: createProxyGetHandler(
68+
path ? path + '.' + prop.toString() : prop.toString(),
69+
cb
70+
),
6371
})
6472
}
6573
return target[prop]
@@ -70,18 +78,7 @@ export function proxifyEffects<Effects>(
7078
effects: Effects,
7179
cb: (effect) => void
7280
): Effects {
73-
return Object.keys(effects).reduce(
74-
(currentEffects, key) => {
75-
const effect = effects[key]
76-
if (isObject(effect)) {
77-
currentEffects[key] = new Proxy(effect, {
78-
get: createProxyGetHandler(key, cb),
79-
})
80-
} else {
81-
currentEffects[key] = effect
82-
}
83-
return currentEffects
84-
},
85-
{} as Effects
86-
)
81+
return new Proxy(effects, {
82+
get: createProxyGetHandler('', cb),
83+
})
8784
}

0 commit comments

Comments
 (0)