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

Recheck rv() against previous value in useEffect callback in useReactiveVar #8135

Merged
merged 3 commits into from
May 4, 2021

Conversation

benjamn
Copy link
Member

@benjamn benjamn commented May 4, 2021

While rereading the code @jcreighton added in #7652, I remembered it's safe to access a reactive variable rv by calling rv() in a useEffect callback (without going through useReactiveVar).

Taking advantage of this insight, we should be able to compare the latest variable value to the previous value before deciding whether to call rv.onNextChange(setValue) in the effect callback function, if/when that callback fires. If the values already disagree, we can skip listening and just call setValue right away.

In React <StrictMode>, there will be two initial rv() values generated prior to the useIsomorphicEffect callback (which may never fire), but those values should always be the same, unless the reactive variable somehow gets updated in between the double renders. I don't think that's possible, since React invokes those renders synchronously, one after the other.

The key difference between this approach and calling rv.onNextChange synchronously in render is that (in this new approach) we can safely ignore the extra value without doing anything to clean up its resources, whereas calling rv.onNextChange in render made us additionally responsible for calling the extra cancel function returned by the extra rv.onNextChange call, which was not easy/possible.

If I'm on the right track here, then we finally have a way to "listen" for reactive variable changes that happen before the effect callback fires, even if that callback is noticeably delayed.

While rereading the code @jcreighton added in #7652, I remembered it's
safe to call rv() in a useEffect callback (without useReactiveVar),
which allows us to compare the latest variable value to the previous
value before deciding whether to call rv.onNextChange(setValue) in the
effect callback function. If the values already disagree, we can skip
listening and just call setValue right away.

In React <StrictMode>, the two initial rv() values generated prior to
the useIsomorphicEffect callback should always be the same, unless the
reactive variable somehow gets updated in between those renders, which I
don't think is possible, since React invokes those renders sychronously,
one after the other:
https://github.com/facebook/react/blob/0e100ed00fb52cfd107db1d1081ef18fe4b9167f/packages/react-reconciler/src/ReactUpdateQueue.new.js#L391-L399

The key difference between this approach and calling rv.onNextChange
synchronously in render is that (in this new approach) we can ignore the
extra value without doing anything to clean up its resources, whereas
with rv.onNextChange we were also responsible for calling the extra
cancel function produced by the extra rv.onNextChange call.
@benjamn benjamn requested review from jcreighton and brainkim May 4, 2021 21:48
@benjamn benjamn self-assigned this May 4, 2021
@benjamn benjamn changed the title Recheck rv() against previous value in useIsomorphicEffect. Recheck rv() against previous value in useEffect callback in useReactiveVar. May 4, 2021
@benjamn benjamn changed the title Recheck rv() against previous value in useEffect callback in useReactiveVar. Recheck rv() against previous value in useEffect callback in useReactiveVar May 4, 2021
@brainkim
Copy link
Contributor

brainkim commented May 4, 2021

Hurts my brain but the tests pass! Nice to cut down the comments in that hook.

Copy link
Contributor

@jcreighton jcreighton left a comment

Choose a reason for hiding this comment

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

LGTM

@benjamn benjamn merged commit ac25b55 into main May 4, 2021
@benjamn benjamn deleted the simplify-useReactiveVar-again branch May 4, 2021 23:58
CHANGELOG.md Show resolved Hide resolved
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants