Skip to content
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

feat: derivationRequiresObservable & observablesRequiresReaction #2079

Merged
merged 2 commits into from
Sep 30, 2019

Conversation

Bnaya
Copy link
Member

@Bnaya Bnaya commented Aug 18, 2019

PR checklist:

  • Added unit tests
  • Updated changelog
  • Updated docs (either in the description of this PR as markdown, or as separate PR on the gh-pages branch. Please refer to this PR). For new functionality, at least API.md should be updated
  • Added typescript typings
  • Verified that there is no significant performance drop (npm run perf)
  • Backport to mobx 4 branch (?)

Feel free to ask help with any of these boxes!

The above process doesn't apply to doc updates etc.

@coveralls
Copy link

coveralls commented Aug 18, 2019

Coverage Status

Coverage increased (+0.09%) to 93.381% when pulling b1a30f5 on observables-requires-derivation into 81c1bcd on master.

Copy link
Contributor

@danielkcz danielkcz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff indeed. Got myself trapped by both of these caveats couple of times too.

.vscode/launch.json Outdated Show resolved Hide resolved
.vscode/settings.json Outdated Show resolved Hide resolved
src/api/configure.ts Outdated Show resolved Hide resolved
src/core/derivation.ts Outdated Show resolved Hide resolved
@Bnaya Bnaya force-pushed the observables-requires-derivation branch from 8a253eb to bc95364 Compare August 19, 2019 12:03
@Bnaya Bnaya marked this pull request as ready for review August 19, 2019 12:13
@Bnaya
Copy link
Member Author

Bnaya commented Aug 19, 2019

I'm not sure how to update the changelog, do we have any script that do that, or manually?

@Bnaya Bnaya requested a review from danielkcz August 19, 2019 12:40
@danielkcz
Copy link
Contributor

I'm not sure how to update the changelog, do we have any script that does that, or manually?

Still manually. And tick other checkboxes as well when completed :)

@danielkcz
Copy link
Contributor

danielkcz commented Aug 19, 2019

I see that 5.13.1 / 4.13.1 is still unpublished, but considering this is a new feature it should go into 5.14.0 / 4.14.0 and we shall publish before merging this I suppose.

Bnaya added a commit that referenced this pull request Aug 19, 2019
derivationRequiresObservable & observablesRequiresReaction
@Bnaya Bnaya changed the title Observables requires derivation Observables requires derivation & derivationRequiresObservable Aug 19, 2019
@Bnaya Bnaya changed the title Observables requires derivation & derivationRequiresObservable feat: derivationRequiresObservable & observablesRequiresReaction Aug 19, 2019
@Bnaya
Copy link
Member Author

Bnaya commented Aug 19, 2019

I've pushed the changelog update

@urugator
Copy link
Collaborator

Change observablesRequiresReaction to observableRequiresReaction
so it's gramatically correct and matches other two

Change derivationRequiresObservable to reactionRequiresObservable
to match the other two

requiresObservable option should be added to reaction/autorun (similary to computed's requiresReaction)

Note on defaults:
observableRequiresReaction can't be true by default, because actions are optional by default and working with observable models outside of reactive context is completely valid (in practice I think it can be useful to turn this on after init logic).

reactionRequiresObservable - while reactions without observables are de-facto an error, being able to slap observer everywhere without thinking about deps is also useful

Could you add some test cases where observables are accesses indirectly (through computed) or inside action (eg inside autorun)?

@Bnaya
Copy link
Member Author

Bnaya commented Aug 19, 2019

requiresObservable option should be added to reaction/autorun (similary to computed's requiresReaction)

Can you please elaborate?

Note on defaults:
observableRequiresReaction can't be true by default, because actions are optional by default and working with observable models outside of reactive context is completely valid (in practice I think it can be useful to turn this on after init logic).

reactionRequiresObservable - while reactions without observables are de-facto an error, being able to slap observer everywhere without thinking about deps is also useful

I personally think it's not a good practice, and the developer should be aware to what gonna render and why.
And when you are working in environment of mixed react & mobx state it can get really messy.
I don't want "dumb" components to be wrapped with observer
If i could do that in the type system/static analysis, i would totally do.
But i'm really OK with keeping them both optional.

Could you add some test cases where observables are accesses indirectly (through computed) or inside action (eg inside autorun)?

I will

@urugator
Copy link
Collaborator

Can you please elaborate?

Ability to configure this per reaction - reaction(deriv, effect, { requiresObservable: true })
Related: #1197 (comment)

OT:

the developer should be aware to what gonna render and why

Why not to use explicit subscription then?

@danielkcz
Copy link
Contributor

being able to slap observer everywhere without thinking about deps is also useful

I also don't agree, why would someone want to deoptimize on purpose? I think it comes more from the current lack of validation fixed by this PR that kinda forces you to just "slap observer everywhere". I don't see an actual reason for doing it otherwise.

That said, with requiresObservable on reactions would give that choice which is fine. I would probably enable it by default for observer anyway. And it should fall back to the global setting if there is none specified for a reaction.

src/core/observable.ts Outdated Show resolved Hide resolved
@urugator
Copy link
Collaborator

The lib is all about automatic subscription management. The idea is that you don't have to worry about what depends on what and you shouldn't experience staleness once you fulfill the contract - observable on data and observer on component.
Making everything observable/observer is an easy to follow and straithforward way to do that.

comes more from the current lack of validation

Agree, but as mentioned the fix is not generally applicable, eg it makes sense only with enforceAction('strict') and I am afraid there may be some false positives.

What's the expected behavior of:

mobx.configure({ observablesRequiresReaction: true })
autorun(() => {
  untracked(() => x.y);
})

Can you add it to tests as well please?

How do you feel about labelling this "experimental" and waiting for some feedback?

@Bnaya
Copy link
Member Author

Bnaya commented Aug 20, 2019

I prefer to keep it optional and it's totally can be marked experimental so we could tweak the behaviour later without breaking changes.

reaction(deriv, effect, { requiresObservable: true })

We will need to also change mobx-react api to expose also that
should it fail or warn? looking at computedvalue code, i think there's something not optimal there.
on dev time it will fail and on prod time it will just work.
its different behaviour between prod & dev, not just warning on dev
https://github.com/mobxjs/mobx/blob/master/src/core/computedvalue.ts#L270-L289

@urugator
Copy link
Collaborator

Ignore on production for performance sake I suppose...
I think that the global configuration only warns (rather failing) so there is some space for intentionally exceptional behavior (some workaround perhaps) without crashing the app.

@Bnaya
Copy link
Member Author

Bnaya commented Aug 20, 2019

Another thing that came up in my mind:
Add warning when action didn't change any observable value.
But maybe better keep to to another PR?

@urugator
Copy link
Collaborator

Add warning when action didn't change any observable value.

The change can be conditional or deferred:

@action doSomething(a) {
  if (this.cond) {
     this.x = a;
  }
}

@action doSomething() {
  // action is untracked to avoid accidental subscription (so it's not entirely useless even if you don't change anything):
  const param = this.y;
  xhr.post(param).then(() => this.x = "a");
}

@Bnaya Bnaya requested a review from urugator August 22, 2019 20:55
@Bnaya
Copy link
Member Author

Bnaya commented Aug 22, 2019

changes pushed
After we will settle this PR, i will make the respective changes to mobx 4 & docs PRs

test/base/strict-mode.js Outdated Show resolved Hide resolved
test/base/strict-mode.js Outdated Show resolved Hide resolved
@Bnaya
Copy link
Member Author

Bnaya commented Sep 23, 2019

@urugator friendly ping :)
Just in case you've missed it, i know it took me long time

src/api/transaction.ts Outdated Show resolved Hide resolved
src/core/derivation.ts Outdated Show resolved Hide resolved
@urugator
Copy link
Collaborator

urugator commented Sep 24, 2019

Just to clarify... I didn't mind that it didn't warn inside untracked, I was concerned by the reason why it didn't warn.
It didn't warn because there was a batch, but it shouldn't depend on a presence of batch ... it should depend on the presence of derivation/action - and both derivation/action was missing - so at that time one would expect it to warn (regardless of what the actual behavior should be)... Hope it makes sense.

@urugator
Copy link
Collaborator

urugator commented Sep 24, 2019

@mweststrate you may want to take a look at this.

To summarize:
2 new global configuration options:

reactionRequiresObservable: boolean = false

  • warns when derivation.observing.length === 0
  • also configurable per individual reaction/autorun/when (via requiresObservable option)
  • motivation: help the developer to be aware of the reactive parts of the code, help to catch cases when you touch the observable only after if that wasn't true on the first run.
  • implemented as simple check inside track function

observableRequiresReaction: boolean = false

  • warns when any observable is acccessed outside of derivation or action
  • motivation: reveal dereferencing outside of observer (eg passing to 3rd party lib, forgetting observer, dereferencing too soon, etc)
  • implemented similary to enforceAction - global flag allowStateReads, switched in start(end)Action/trackDerivedFunction, checked in reportObserved
  • unsure about the option name, the most descriptive name would be readingObservableRequiresReactionOrAction, input welcome (enforceReaction, ...??)

Both currently labelled as experimental.

@mweststrate
Copy link
Member

mweststrate commented Sep 24, 2019 via email

@Bnaya Bnaya force-pushed the observables-requires-derivation branch from 7ed2f27 to 371a733 Compare September 24, 2019 20:42
Bnaya added a commit that referenced this pull request Sep 24, 2019
@Bnaya Bnaya force-pushed the observables-requires-derivation branch from 371a733 to abf4f15 Compare September 24, 2019 21:26
@Bnaya
Copy link
Member Author

Bnaya commented Sep 24, 2019

I've updated the mobx 4 backport PR

Copy link
Member

@mweststrate mweststrate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great so far! Left some comments, but nothing big :)

src/api/configure.ts Show resolved Hide resolved
src/core/derivation.ts Show resolved Hide resolved
src/core/derivation.ts Outdated Show resolved Hide resolved
src/core/observable.ts Show resolved Hide resolved
@mweststrate
Copy link
Member

mweststrate commented Sep 30, 2019 via email

@Bnaya Bnaya force-pushed the observables-requires-derivation branch from c91bbbd to 8c070a9 Compare September 30, 2019 12:10
Bnaya added a commit that referenced this pull request Sep 30, 2019
@Bnaya Bnaya force-pushed the observables-requires-derivation branch from 8c070a9 to b1a30f5 Compare September 30, 2019 12:20
@Bnaya Bnaya merged commit 82c67f8 into master Sep 30, 2019
@Bnaya Bnaya deleted the observables-requires-derivation branch September 30, 2019 12:24
Bnaya added a commit that referenced this pull request Sep 30, 2019
derivationRequiresObservable & observablesRequiresReaction
Bnaya added a commit that referenced this pull request Sep 30, 2019
reactionRequiresObservable & observablesRequiresReaction
@Bnaya
Copy link
Member Author

Bnaya commented Sep 30, 2019

I've merged the PRS!

What's the policy regarding publishing?
also, i wish we had something that publishes canary versions for each PR or so

@mweststrate
Copy link
Member

mweststrate commented Oct 1, 2019 via email

@mweststrate
Copy link
Member

Released!

mweststrate added a commit that referenced this pull request Oct 2, 2019
@Bnaya
Copy link
Member Author

Bnaya commented Oct 2, 2019

I've created a blog post regarding these features:
https://medium.com/@bnaya/mobx-5-14-0-will-help-you-stay-reactive-and-only-when-you-need-to-2e897d3bfabe
I would love to have some feedback before publishing

@mweststrate
Copy link
Member

mweststrate commented Oct 3, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants