-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Action is not transactional for computeds, but docs says it should be #3017
Comments
computed is postponed until the end of the transaction, *unless* it is read
by something before the end of the transaction, to make sure no
inconsistent world is shown ever, and computed values can be read safely as
input for further decision logic. If you remove the `this.full` in your
console.logs, and instead put a console.log in full itself, you will see it
won't be called eagerly.
…On Tue, Jun 29, 2021 at 12:32 AM Jasper ***@***.***> wrote:
*Intended outcome:*
During the execution of an action, computeds will not be recomputed, as
per the docs on action <https://mobx.js.org/actions.html>
No observers will be updated until the outer-most action has finished,
guaranteeing that intermediate or incomplete values produced during an
action are not visible to the rest of the application until the action has
completed.
and transaction <https://mobx.js.org/api.html#transaction>.
Used to batch a bunch of updates without notifying any observers until the
end of the transaction.
and old action docs <https://www.mobxjs.com/refguide/action.html>
actions will batch mutations and only notify computed values and reactions
after the (outer most) action has finished.
Now, this is assuming that the term observer is used as in the rest of
the code base: computeds & reactions. So not the mobx-react terminology,
where it is just a reaction.
*Actual outcome:*
Computeds get recomputed during an action.
*How to reproduce the issue:*
https://jsfiddle.net/jasperh/w6bkthu9/8/
const { observable, autorun, action } = mobx;
const obs = observable({
first: 'John',
last: 'Doe',
get full() {
return this.first + ' ' + this.last;
},
update: action(function(e) {
this.first = 'Pete';
console.log('during action', this.full);
this.last = 'Moe';
console.log('during action', this.full);
}),
})
autorun(() => console.log('reaction', obs.full));
obs.update();
Gives console:
reaction John Doe
during action Pete Doe
during action Pete Moe
reaction Pete Moe
*Versions*
MobX 5 & 6
** extra info **
Found this while researching a problem I described in this discussion
<#3007>.
I guess docs should be updated that only reaction and not computed is
postponed.
Though I'd still like an action that is fully transactional, including wrt
computeds (and then maybe even observable properties?).
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#3017>, or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAN4NBG7BNYPVWEIAFPFXKLTVEBCLANCNFSM47O6JQ2Q>
.
|
Sure, but... computeds are never called eagerly, they're always lazy, regardless of transaction context? In any case, if this is expected behaviour, docs should maybe say: Action:
Transaction:
Since at least computeds seemingly áre notified, they just remain lazy, like they always are; They will recompute when stale & read (in either an I can make a PR for that? Would you be interested to include a computed-included transaction functionality in mobx ( I could try, though it seems a deep dive :D)? If not, can you give me a pointer how I could implement it myself? |
Sure!
Nope, such a model could become easily too finicky to reason about and support. E.g. some code reading a computed would suddenly become buggy because a (totally unrelated and unaware) transaction higher in the stack changes the behavior of computeds. Note that you can set this option to forbid uncached reads: https://mobx.js.org/computeds.html#requiresreaction. I'd use it only very sparingly. |
Closer description of actual implementation.
Oh yeah, I didn't mean to change current Do you reckon it would take a big refactor or should it be a relatively small change, if I'd try to do it myself?
Thanks, though I'm not sure how that would help in this situation, I'm always in reactive context when reading. PR for docs: #3018 |
What I mean is that the behavior of
In the above example it will now become completely unpredictable what Note that if computation postponement is really important in your specific case, you can convert the computed to a reaction + observable: |
Right, I understand, that's insightful, thanks for elaborating : ). Logic of Here's a fibonacci example where the computed-updates transaction can cause confusion if you're not very familiar with the reactive paradigm, as normally the action's code would work. The solution is of course just dereferencing https://jsfiddle.net/jasperh/w6bkthu9/25/ const { observable, autorun, action, toJS } = mobx;
const fib = observable({
n_min1: 1,
n_min2: 1,
i: 2,
get n() {
return this.n_min1 + this.n_min2;
},
update: action(function() {
this.i++;
this.n_min2 = this.n_min1;
this.n_min1 = this.n;
}),
})
autorun(() => console.log(`fib no ${fib.i}: ${fib.n}`, toJS(fib)));
fib.update();
fib.update();
fib.update();
Yup, but now other reactions can get inconsistent state (celcius <-> fahrenheit) if they run before this reaction. And we can't control reaction order. Same problem as I mentioned in the discussion before the weekend. I guess I'm just going with the current behaviour and tell users of our library to dereference any state before updating state in the actions they write. |
I guess you can let each action dictate its own behavior. Anything set in
|
MobX behaves here no differently than how JavaScript itself would behave, so I don't see really what there is to warn about :) |
Right, if you keep I was thinking more of a non-mobx implementation that would go for In our case we need to warn since it's less obvious than this example. In short: accessing a model also observes a Sending async in a reaction is not an option either because we can't control order of reaction execution. I've tried to think of other refactors that would remove the state reading in model access. That would solve it too, for now at least, alas no solution found yet : ). |
I'll close this as the issue itself is solved. |
Intended outcome:
Computeds will not be recomputed during an action, as per the docs on
action
and
transaction
.and old action docs
Now, this is assuming that the term
observer
is used as in the rest of the code base:computed
s &reaction
s. So not themobx-react
terminology, where it is just areaction
.Actual outcome:
Computeds get recomputed during an action.
How to reproduce the issue:
https://jsfiddle.net/jasperh/w6bkthu9/8/
Gives console:
Versions
MobX 5 & 6
extra info
Found this while researching a problem I described in this discussion.
I guess docs should be updated that only
reaction
and notcomputed
is postponed.Though I'd still like an action that is fully transactional, including for computeds.
The text was updated successfully, but these errors were encountered: