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

Refactor layout phase to use recursion #24906

Merged
merged 4 commits into from Jul 12, 2022

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented Jul 12, 2022

This converts the layout phase to iterate over its effects recursively instead of iteratively. This makes it easier to track
contextual information, like whether a fiber is inside a hidden tree.

We already made this change for the mutation phase in #24308. This PR follows a similar structure, though is a bit more straightforward.

Because this is largely a structural refactor as opposed to a logical one, I would like to attempt landing this using the same strategy as #24308: land the stack directly in main, without a feature flag. If there's a bug somewhere, it should be easy to bisect and fix forward.

If it turns out to be trickier than expected, we can revert and use the reconciler fork infra instead. I don't want to jump straight to using the fork infra because it will make it harder for other contributors to land changes to the commit phase in the meantime.

Only certain fiber types can have refs attached to them, so this moves the
Ref effect logic out of the common path and into the corresponding branch
of the layout phase's switch statement.

The types of fibers this affects are host components and class components.
Function components are not affected because they can only have a ref via
useImperativeHandle, which has a different implementation. The experimental
Scope type attaches its refs in the mutation phase, not the layout phase.
The fiber tag is more specific than the effect flag, so we should always
refine the type of work first, to minimize redundant checks.
(This is the same as f9e6aef, but for the layout phase rather than the
mutation phase.)

This moves the try-catch from around each fiber's layout phase to
direclty around each user function (effect function, callback, etc).

We already do this when unmounting because if one unmount function
errors, we still need to call all the others so they can clean up
their resources.

Previously we didn't bother to do this for anything but unmount,
because if a mount effect throws, we're going to delete that whole
tree anyway.

But now that we're switching from an iterative loop to a recursive one,
we don't want every call frame on the stack to have a try-catch, since
the error handling requires additional memory.

Wrapping every user function is a bit tedious, but it's better
for performance. Many of them already had try blocks around
them already.
This converts the layout phase to iterate over its effects
recursively instead of iteratively. This makes it easier to track 
contextual information, like whether a fiber is inside a hidden tree.

We already made this change for the mutation phase. See 481dece for
more context.
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Jul 12, 2022
@sizebot
Copy link

sizebot commented Jul 12, 2022

Comparing: e225fa4...b641d02

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 133.13 kB 133.08 kB = 42.76 kB 42.71 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 138.41 kB 138.35 kB = 44.34 kB 44.27 kB
facebook-www/ReactDOM-prod.classic.js = 465.92 kB 465.89 kB = 84.38 kB 84.16 kB
facebook-www/ReactDOM-prod.modern.js = 451.16 kB 451.13 kB = 82.14 kB 81.91 kB
facebook-www/ReactDOMForked-prod.classic.js = 465.92 kB 465.89 kB = 84.38 kB 84.16 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react-test-renderer/umd/react-test-renderer.production.min.js = 95.23 kB 95.01 kB = 29.70 kB 29.67 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.min.js = 94.98 kB 94.74 kB = 29.39 kB 29.34 kB
oss-stable/react-test-renderer/umd/react-test-renderer.production.min.js = 90.81 kB 90.59 kB +0.06% 28.39 kB 28.41 kB
oss-stable-semver/react-test-renderer/umd/react-test-renderer.production.min.js = 90.79 kB 90.56 kB +0.06% 28.39 kB 28.41 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.min.js = 90.55 kB 90.32 kB = 28.08 kB 28.04 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.min.js = 90.53 kB 90.29 kB = 28.08 kB 28.04 kB

Generated by 🚫 dangerJS against b641d02

Copy link
Member

@rickhanlonii rickhanlonii left a comment

Choose a reason for hiding this comment

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

LGTM!

}
case OffscreenComponent: {
const isModernRoot = (finishedWork.mode & ConcurrentMode) !== NoMode;
if (isModernRoot) {
Copy link
Member

Choose a reason for hiding this comment

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

Do we typically inline this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What do you mean? Like as opposed to outside the switch statement? Generally I like to put these types of things as local as possible so that it's only checked in the branches that need it

@acdlite acdlite merged commit b641d02 into facebook:main Jul 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants