3.x
In MobX 3.x, Writing async actions is a little verbose.
import { observable, runInAction } from 'mobx'
import { someApi } from '../api'
class SomeStore {
@observable someState = ''
async someAction() {
const res = await someApi.fetch()
runInAction(() => {
this.someState = res.data
})
}
}
4.x
MobX 4.x has a new API named flow
, which is really confusing. Every time I google mobx flow
, Facebook's static typing tool shows up.
import { observable, flow } from 'mobx'
import { someApi } from '../api'
class SomeStore {
@observable someState = ''
someAction = flow(function * () {
this.someState = yield someApi.fetch()
})
}
Ohnestly I don't know what is happening above, but the code become shorter and cooler with generator function and class property.
If we use using MobX's flow + TypeScript with strict mode, try bind this to the class in generator function, like this:
public someAction = flow(function * (this: SomeStore) {
this.someState = yield someApi.fetch()
})
(I spend one hour to find this solution)
Promise
If we consider error handling, Promise become simpler:
import { observable, action } from 'mobx'
import { someApi } from '../api'
class SomeStore {
@observable someState = ''
@observable error = null
someAction() {
someApi.fetch().then(
action(res => this.someState = res.data),
action(err => this.err = err),
)
}
}
Top comments (4)
Hello,
just to clarify your examples (and my knowledge of that):
In the first example with async/await you don't need to wrap it in
runInAction
It can be simple as with flow.
runInAction just batch all changes inside the anonymous function and trigger only one update at the end of that function. And because there is just one it is not needed.
But. Flow with generator function does this behind scene because of generator function runs blocks of code between each
yield
separately and thus it is automatically wrapped inrunInAction
.Maybe extend your examples with more setters to make it more clear (add loader, error etc) :)
Thank you for the comment, David!
I do not have to use
runInAction
in this use case right?Maybe that's true.
Actually I wrote multiple MobX state change like this with
mobx.useStrict(true)
:as you pointed out, runInAction wrap
action(fn)()
with friendly syntax, whileflow
andyield
block the state change in async flow.When I faced the MobX's cation of
@action
andrunInAction
, I started to userunInAction
so I would have to understand how MobX change states.Anyway your comment improve my comprehension of MobX!
Thanks again.
I lied to you before :( I didn't read it correctly.
It would work as expected because there is just one setter and mobx can handle that.
But when we turn on
mobx.configure({ enforceActions: true })
it would fail. And it would fail even with@action async someAction() {....}
becauseawait
is asynchronous and outside the original @action function. In this examplethis.someState = await
waits toawait
. So your original example was correct :)It really depends on many things (enforceActions: true, @async decorator, etc ...). Great explanation on this (new for me) page mobx.js.org/best/actions.html :)
No, I did not write
enforceActions: true
(noruseStrict()
, old api) in my article. It's my fault, though thank you for clarification ;)As you mentioned, both
await
andPromise
wrap new function which change MobX's state sorunInAction
, otherwise MobX shows caution.