loading plugin counts the number of outstanding effects per effect, module, and global#245
loading plugin counts the number of outstanding effects per effect, module, and global#245ShMcK merged 4 commits intorematch:masterfrom
Conversation
package.json
Outdated
| "dependencies": { | ||
| "redux": "^3.7.2" | ||
| "redux": "^3.7.2", | ||
| "redux-persist": "^5.9.1" |
There was a problem hiding this comment.
This was a strange thing, but some tests were failing without it...... dunno how it was working before?
There was a problem hiding this comment.
I noticed that in your PR. Tests "should" pass with 'redux-persist' as a dev dependency.
| }, | ||
| effects: { | ||
| async timeout() { | ||
| await delay(200) |
There was a problem hiding this comment.
a more standard async delay call
| dispatch.count.timeout() | ||
| dispatch.count.timeout() | ||
| expect(store.getState().loading.global).toBe(true) | ||
| expect(store.getState().loading.global).toBe(2) |
There was a problem hiding this comment.
this is the gist of the new functionality
|
|
||
| dispatch.count.timeout() | ||
| dispatch.count.timeout() | ||
| expect(store.getState().loading.effects.count.timeout).toBe(2) |
There was a problem hiding this comment.
this is the gist of the new functionality
plugins/loading/test/loading.test.js
Outdated
| dispatch.count.timeout2() | ||
| expect(store.getState().loading.effects.count.timeout1).toBe(1) | ||
| expect(store.getState().loading.effects.count.timeout2).toBe(1) | ||
| expect(store.getState().loading.global).toBe(2) |
There was a problem hiding this comment.
making sure global increments for every dispatched effect
| await origEffect(...props) | ||
| // waits for dispatch function to finish before calling "hide" | ||
| } finally { | ||
| dispatch.loading.hide({ name, action }) |
There was a problem hiding this comment.
omit catch, but decrements counter even on an error
|
Note that the loading plugin will likely require some changes once #236 is merged. Working on this at the moment. At that point, loading can be handled with 2 actions, rather than the current 3.
|
|
@crazy4groovy - interesting idea - an integer count is certainly more useful than a boolean. I would use this, for its debugging value alone. The only issue I can see is that users with typechecking (TypeScript, Flow) may find it annoying. Do you think it's possible to make it an "opt in" feature? |
|
@ShMcK interesting point about typechecking. Honestly I'm not strong on the topic so I don't have any insight, sorry. But I didn't see any Boolean -> number is a breaking change, so opt-in would help maintain backwards-compatibility. That is often a smart move, unless the added feature completely trumps the existing functionality. Can you see any case where the new functionality is not better than current? Alternatively a v1.x (semver) bump may be the right move. Thoughts? |
Nope. You have a good idea.
In JS, there is no issue with dynamically typing variables. var loading = 0
if (loading) {
// do something
}But when using TypeScript, for example, the compiler expects variables to be statically typed. var loading = 0
if (loading) { // warning, expected a boolean
// do something
}Then you have to do some nasty coercion to make the compiler happy. var loading = 0
if (!!loading) {
// do something
}I believe these two solutions can co-exist very simply.
What do you think? |
I see. Yes, in JS world (or TS?) I would just expect to check loading like this: var loading = 0
if (loading > 0) { // always boolean
// do something
}I'm not opposed to a config param here, but it's not obvious to me how to store state as a number, and retrieve it as a boolean. Except maybe a selector of some kind? A hint here would be interesting for me. |
…odule, and global
|
^^ poke? |
|
Sorry, I'll need a bit more time. I'll try to get back to you later this evening. |
|
I think both solutions can co-exist. I came up with something like this: const createLoadingAction = (valueFn) =>
(show) =>
(state, { name, action }: any) => ({
...state,
global: valueFn[show](state.global),
models: {
...state.models,
[name]: valueFn[show](state.models[name]),
},
effects: {
...state.effects,
[name]: {
...state.effects[name],
[action]: valueFn[show](state.effects[name][action]),
},
},
})
let valueFn
let defaultValue
if (config.valueType === 'number') {
valueFn = {
hide: () => 0,
show: (v) => v + 1,
}
defaultValue = 0
} else {
valueFn = {
hide: () => false,
show: () => true,
}
defaultValue = false
}
const loadingAction = createLoadingAction(valueFn)
const loading: Model = {
name: loadingModelName,
reducers: {
hide: loadingAction('hide'),
show: loadingAction('show'),
},
state: {
effects: {},
global: defaultValue,
models: {},
},
}
/* add defaultValue where needed */In this case, users can opt in to using numbers over booleans by adding a This way, TypeScript/Flow folks are happy, no breaking changes, and the README can emphasize that numbers are an excellent option. What do you think? |
|
Interesting idea! Feels pretty complicated but it's an interesting concept, I'll try to take a look this weekend. Also, I think the numerical |
|
Great, also have a look at #266. Can you think of a way to resolve this with the loading plugin? |
|
|
||
| const loadingModelName = config.name || 'loading' | ||
|
|
||
| const converter = |
There was a problem hiding this comment.
I'm pretty happy with this solution, it keeps a "background" counter called cntState of dispatched effects, but converts it onto its public state as either a number or boolean.
| @@ -0,0 +1,327 @@ | |||
| const delay = ms => new Promise(r => setTimeout(r, ms)) | |||
There was a problem hiding this comment.
There are a lot more tests now.. I just duplicated them and tested for boolean in one file, and number in this one.
| ## Options | ||
|
|
||
| ### name | ||
| ### asNumber |
There was a problem hiding this comment.
yay for docs! 👍
| import example from './example' | ||
|
|
||
| const loadingPlugin = createLoadingPlugin() | ||
| const loadingPlugin = createLoadingPlugin({ asNumber: true }) |
There was a problem hiding this comment.
The example uses numbers, I think it's way more practical personally
|
Quite an elegant solution with the "converter". Thanks for putting in all the effort @crazy4groovy, great work, and sorry this took so long. I really appreciate your patience. |
|
Getting a strange error from the Rollup bundler. There is no "parse" in that file. WIll have to look at this tomorrow morning before I'm able to publish as |
|
Drat sorry. Here's a 100% shot in the dark to try: could be the |
|
Maybe this issue: rollup/rollup-plugin-commonjs#299 |
|
Something changed in Rollup. Before I could use the peer dependencies in the root "rematch" directory, but now I had to install them all as dev dependencies in the plugins. |
|
Thanks for your help. Published as @rematch/loading@0.4.0. |
I was having problems with the loading module resolving a pending effect too early, because out of a batch of dispatched effects, the first completed one set the loading state to
false.This implements an integer counter instead of a boolean value.
Also cleaned up the plugin tests slightly.