-
-
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
Better React 18 support #3590
Better React 18 support #3590
Conversation
🦋 Changeset detectedLatest commit: e55c51c The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@@ -35,23 +37,7 @@ exports[`Redeclaring an existing observer component as an observer should log a | |||
} | |||
`; | |||
|
|||
exports[`SSR works #3448 1`] = ` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was not supposed to warn. Warning was caused by calling disableStaticRendering
before unmount
in the test. Therefore componentWillUnmount
used logic for non-static rendering.
@@ -102,7 +102,7 @@ describe("inject based context", () => { | |||
expect(C.displayName).toBe("inject(ComponentC)") | |||
}) | |||
|
|||
test.only("shouldn't change original displayName of component that uses forwardRef", () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was left in here for quite some time by mistake.
/** | ||
* If props are shallowly modified, react will render anyway, | ||
* so atom.reportChanged() should not result in yet another re-render | ||
*/ | ||
setHiddenProp(this, skipRenderKey, false) | ||
/** | ||
* forceUpdate will re-assign this.props. We don't want that to cause a loop, | ||
* so detect these changes | ||
*/ | ||
setHiddenProp(this, isForcingUpdateKey, false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merged these together into isUpdating
as they basically handle the same thing:
One is set by reaction to prevent reportChanged on props
setter,
second is set by props
setter to prevent reaction.
So both says "I am already updating, do not cause another update"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good so far! I didn't review everything yet (still have to do the new observer implementations), but submitting this review already so that you can see the comments, as coming days I probably won't be able to review further with all Christmas activities :).
Merry Christmas and happy new year!
packages/mobx-react-lite/src/utils/UniversalFinalizationRegistry.ts
Outdated
Show resolved
Hide resolved
packages/mobx-react-lite/src/utils/UniversalFinalizationRegistry.ts
Outdated
Show resolved
Hide resolved
"dependencies": {}, | ||
"dependencies": { | ||
"use-sync-external-store": "^1.2.0" | ||
}, | ||
"peerDependencies": { | ||
"mobx": "^6.1.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
peer dependency probably has to be bumped to make sure globalState.stateVersion is available?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current impl should be BC, see
https://github.com/mobxjs/mobx/pull/3590/files#diff-6b93715e19eb9119588dbe2af609a132062e61484d0d3de41b9e32e2d4a4ad5cR13-R17
https://github.com/mobxjs/mobx/pull/3590/files#diff-6b93715e19eb9119588dbe2af609a132062e61484d0d3de41b9e32e2d4a4ad5cR31-R34
https://github.com/mobxjs/mobx/pull/3590/files#diff-6b93715e19eb9119588dbe2af609a132062e61484d0d3de41b9e32e2d4a4ad5cR76-R79
but bumping dep is a way too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that is pretty neat actually! I'd still bump the peerDependency (as the warnings are often ignored), and make this change itself a major version, since it might affect semantics and spreads risk a bit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is gonna be a major should it still be focused on React 18 support with otherwise minimal changes, or should this be an opportunity to introduce larger changes? (removing deprecated APIs, hooks, inject, options, cleanups, etc ...)
I assume the former.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can do the former; decorators are also around the corner, an I expect after that is a better moment to do a bigger clean up (e.g. legacy decorator support etc)
|
||
test("state version updates correctly", () => { | ||
// This test was designed around the idea of updating version only at the end of batch, | ||
// which is NOT an implementation we've settled on, but the test is still valid. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds correct to me though :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what you mean. Just to provide some background. I've been looking for a common place where I could update state version. endBatch
(when nesting level gets back to zero) seemed like a good candidate, because it's actually called even for individual mutations, but I was worried about some edge cases, like autorun invoking itself. So I wrote this test and gave it a go. However I quickly realized endBatch
is also often called without any actual mutation, therefore I threw the idea away and moved the version update to reportChanged
, but I kept the test as is. I've left the comment to clarify why the test looks this way.
Every Symbol is unique, so at least requires an allocation and GC.
…On Sat, 24 Dec 2022, 11:54 Eric Masiello, ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In packages/mobx/src/core/atom.ts
<#3590 (comment)>:
> @@ -66,6 +67,9 @@ export class Atom implements IAtom {
public reportChanged() {
startBatch()
propagateChanged(this)
+ // We could update state version only at the end of batch,
+ // but we would still have to switch some global flag here to signal a change.
+ globalState.stateVersion = Symbol()
What makes you say Symbol() is more expensive?
—
Reply to this email directly, view it on GitHub
<#3590 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAN4NBH7IACA5LFB6ZLNHUTWO3I5HANCNFSM6AAAAAATCE5TG4>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
}, | ||
"peerDependencies": { | ||
"mobx": "^6.1.0", | ||
"mobx": "^6.9.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A guess - I don't know the actual version it's gonna be released with. Dunno what's the correct way to handle it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot how to do that properly as well :-P But I think this should work anyway
"mobx": "^6.8.0", | ||
"mobx-react-lite": "^3.4.3", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should become ^6.9?.0 and ^4.0.0 at the time of release. Again not sure how to handle that.
@@ -36,10 +36,10 @@ | |||
}, | |||
"homepage": "https://mobx.js.org", | |||
"dependencies": { | |||
"mobx-react-lite": "^3.4.0" | |||
"mobx-react-lite": "^3.4.3" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should become ^4.0.0 at the time of release. Again not sure how to handle that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can already update it now, since in a local checkout it is symlinked anyway to the right disk location?
If that causes problems; I'm not sure anymore if this happened automatically or not when releasing a master, but otherwise we can immediately release it with a patch afterwards?
@mweststrate Please take a look at #3649 and let me know if I should merge it to master first and incorporate the change to this PR. |
Sorry for the delay! I'll dive into it this weekend |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good! Imho it's okay to merge #3649 after this one.
@@ -36,10 +36,10 @@ | |||
}, | |||
"homepage": "https://mobx.js.org", | |||
"dependencies": { | |||
"mobx-react-lite": "^3.4.0" | |||
"mobx-react-lite": "^3.4.3" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can already update it now, since in a local checkout it is symlinked anyway to the right disk location?
If that causes problems; I'm not sure anymore if this happened automatically or not when releasing a master, but otherwise we can immediately release it with a patch afterwards?
}, | ||
"peerDependencies": { | ||
"mobx": "^6.1.0", | ||
"mobx": "^6.9.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot how to do that properly as well :-P But I think this should work anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AWESOME STUFF!
I pulled this repo locally and configured it to use this branch. I see mostly progress with 1 regression. In the output below, "mobx" is The only regressions I see are:
|
@ericmasiello Cool, thank you for looking into it. Eventually I would love to see mobx as part of that repo. The results are what I was expecting/hoping for. I am not entirely sure about it, but I think level 3 is basically non-supportable by any kind of global state managment, because to do time slicing, you need to be able to split the state into indepedent pieces, which is impossible without any extra knowledge about the state. Eg in redux you would need two stores, two dispatchers etc, who knows absolutely nothing about one another. In mobx you would have to be able to somehow group observables, each group would maintain own version, but probably also own dependency tree, something like |
Correction: it merged! |
Hi @urugator Shall we only deprecate the decorator version? |
@zhantx As you said it would have to be called from
EDIT: removed the last point as I realized it requires patching |
Just want to mention that |
Note that disposeOnUnmount doesn't do anything MobX specific IIRC (it was
just a utility we needed back then as well), so I think you could move it
easily outside to your own utility function?
…On Fri, May 12, 2023 at 12:17 PM Xidorn Quan ***@***.***> wrote:
Just want to mention that disposeOnUnmount is very useful for us, and
it's widely used in our codebase. I'd also note that this utility doesn't
encourage, nor does lack of it discourage the pattern. There exists lots of
old code that simply collects disposers in componentDidMount and invokes
them in componentWillUnmount. disposeOnUnmount helps simplify the code
and ensure that they are disposed properly.
—
Reply to this email directly, view it on GitHub
<#3590 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAN4NBCSFZ3DEXVVR7A2ELLXFYE3RANCNFSM6AAAAAATCE5TG4>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
@upsuper That being said, as long as you're willing to be responsible for maintaing this tool, making sure it will be compatible with future react version (so we aren't in a similar situation year later), making sure it won't generate issues, complicate internals and people will use it correctly, I personally don't mind. We've introduced some of these features in the past to be a bit more convinient here and there, to keep everyone happy and now we are reaping the fruits - we're dealing with React incompatibilities, piling up workarounds on the top of workarounds and introducing breaking changes. |
I think it's fine that we maintain our own version of this utility in our codebase. We use it both for reaction/autorun and for other stuff following a similar pattern. Mainly we want to understand the reason that it's deprecated, so that we know whether our use of it has any fundamental issue, in which case our own version of this utility would suffer the same problem as well. It's good to know that it isn't that the utility really has anything incompatible with React. |
A potential source of some trouble is the need to patch |
mobx
It now keeps track of a global state version, which updates with each mutation.
mobx-react-lite
It now uses
useSyncExternalStore
, which should get rid of tearing (you have to update mobx, otherwise it should behave as previously).Replaced reaction tracking utils with
UniversalFinalizationRegistry
. It works the same way, but I found it hard to orient myself in the original impl, so I rewrote it completely, hopefully for the better. It's also easier to reuse for class components.Improved displayName/name handling as discussed in #3438.
mobx-react (class component)
Reactions of uncommited components are now correctly disposed. (fixes #3492)
Reactions don't notify uncommited components, avoiding the warning. (fixes #3492)
Removed symbol "polyfill" and replaced with actual Symbols.
Removed
this.render
replacement detection + warning.this.render
is no longer configurable/writable (possibly BC *).Reaction is no longer exposed as
component[$mobx]
(possibly BC *)Component instance is no longer exposed as
component[$mobx]["reactComponent"]
(possibly BC *)Deprecated
disposeOnUnmount
, it's not compatible with remounting.Refactored code.
Fixed tests.
(*) BC for non-idiomatic usage or when depending on low level (private?) API.
I will update changeset once the changes settles.