-
Notifications
You must be signed in to change notification settings - Fork 50.5k
Move guarded callback closer to usage #21658
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
Closed
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
45ba73b
Wrap eventhandle-specific logic in a flag
gaearon 161be62
Make guarded callback more local for before mutation phase
gaearon d1e7f28
Make guarded callback more local for mutation phase
gaearon 696c161
Sync forks
gaearon 626cd53
Remove the safeguard around beforeActiveInstanceBlur
gaearon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -26,6 +26,7 @@ import type {OffscreenState} from './ReactFiberOffscreenComponent'; | |||||
| import type {HookFlags} from './ReactHookEffectTags'; | ||||||
|
|
||||||
| import { | ||||||
| enableCreateEventHandleAPI, | ||||||
| enableProfilerTimer, | ||||||
| enableProfilerCommitHooks, | ||||||
| enableProfilerNestedUpdatePhase, | ||||||
|
|
@@ -365,12 +366,16 @@ function commitBeforeMutationEffects_begin() { | |||||
| while (nextEffect !== null) { | ||||||
| const fiber = nextEffect; | ||||||
|
|
||||||
| // TODO: Should wrap this in flags check, too, as optimization | ||||||
| const deletions = fiber.deletions; | ||||||
| if (deletions !== null) { | ||||||
| for (let i = 0; i < deletions.length; i++) { | ||||||
| const deletion = deletions[i]; | ||||||
| commitBeforeMutationEffectsDeletion(deletion); | ||||||
| // This phase is only used for beforeActiveInstanceBlur. | ||||||
| // Let's skip the whole loop if it's off. | ||||||
| if (enableCreateEventHandleAPI) { | ||||||
| // TODO: Should wrap this in flags check, too, as optimization | ||||||
| const deletions = fiber.deletions; | ||||||
| if (deletions !== null) { | ||||||
| for (let i = 0; i < deletions.length; i++) { | ||||||
| const deletion = deletions[i]; | ||||||
| commitBeforeMutationEffectsDeletion(deletion); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -390,24 +395,46 @@ function commitBeforeMutationEffects_begin() { | |||||
| function commitBeforeMutationEffects_complete() { | ||||||
| while (nextEffect !== null) { | ||||||
| const fiber = nextEffect; | ||||||
| if (__DEV__) { | ||||||
| setCurrentDebugFiberInDEV(fiber); | ||||||
| invokeGuardedCallback( | ||||||
| null, | ||||||
| commitBeforeMutationEffectsOnFiber, | ||||||
| null, | ||||||
| fiber, | ||||||
| ); | ||||||
| if (hasCaughtError()) { | ||||||
| const error = clearCaughtError(); | ||||||
| captureCommitPhaseError(fiber, fiber.return, error); | ||||||
| const flags = fiber.flags; | ||||||
|
|
||||||
| if (enableCreateEventHandleAPI) { | ||||||
| if ( | ||||||
| !shouldFireAfterActiveInstanceBlur && | ||||||
| focusedInstanceHandle !== null | ||||||
| ) { | ||||||
| // Check to see if the focused element was inside of a hidden (Suspense) subtree. | ||||||
| // TODO: Move this out of the hot path using a dedicated effect tag. | ||||||
| if ( | ||||||
| fiber.tag === SuspenseComponent && | ||||||
| isSuspenseBoundaryBeingHidden(fiber.alternate, fiber) && | ||||||
| doesFiberContain(fiber, focusedInstanceHandle) | ||||||
| ) { | ||||||
| shouldFireAfterActiveInstanceBlur = true; | ||||||
| beforeActiveInstanceBlur(fiber); | ||||||
| } | ||||||
| } | ||||||
| resetCurrentDebugFiberInDEV(); | ||||||
| } else { | ||||||
| try { | ||||||
| commitBeforeMutationEffectsOnFiber(fiber); | ||||||
| } catch (error) { | ||||||
| captureCommitPhaseError(fiber, fiber.return, error); | ||||||
| } | ||||||
|
|
||||||
| if ((flags & Snapshot) !== NoFlags) { | ||||||
| if (__DEV__) { | ||||||
| setCurrentDebugFiberInDEV(fiber); | ||||||
| invokeGuardedCallback( | ||||||
| null, | ||||||
| commitBeforeMutationEffectsOnFiber, | ||||||
| null, | ||||||
| fiber, | ||||||
| ); | ||||||
| if (hasCaughtError()) { | ||||||
| const error = clearCaughtError(); | ||||||
| captureCommitPhaseError(fiber, fiber.return, error); | ||||||
| } | ||||||
| resetCurrentDebugFiberInDEV(); | ||||||
| } else { | ||||||
| try { | ||||||
| commitBeforeMutationEffectsOnFiber(fiber); | ||||||
| } catch (error) { | ||||||
| captureCommitPhaseError(fiber, fiber.return, error); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -423,121 +450,106 @@ function commitBeforeMutationEffects_complete() { | |||||
| } | ||||||
|
|
||||||
| function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) { | ||||||
| const current = finishedWork.alternate; | ||||||
| const flags = finishedWork.flags; | ||||||
| setCurrentDebugFiberInDEV(finishedWork); | ||||||
|
|
||||||
| if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) { | ||||||
| // Check to see if the focused element was inside of a hidden (Suspense) subtree. | ||||||
| // TODO: Move this out of the hot path using a dedicated effect tag. | ||||||
| if ( | ||||||
| finishedWork.tag === SuspenseComponent && | ||||||
| isSuspenseBoundaryBeingHidden(current, finishedWork) && | ||||||
| doesFiberContain(finishedWork, focusedInstanceHandle) | ||||||
| ) { | ||||||
| shouldFireAfterActiveInstanceBlur = true; | ||||||
| beforeActiveInstanceBlur(finishedWork); | ||||||
| switch (finishedWork.tag) { | ||||||
| case FunctionComponent: | ||||||
| case ForwardRef: | ||||||
| case SimpleMemoComponent: { | ||||||
| break; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if ((flags & Snapshot) !== NoFlags) { | ||||||
| setCurrentDebugFiberInDEV(finishedWork); | ||||||
|
|
||||||
| switch (finishedWork.tag) { | ||||||
| case FunctionComponent: | ||||||
| case ForwardRef: | ||||||
| case SimpleMemoComponent: { | ||||||
| break; | ||||||
| } | ||||||
| case ClassComponent: { | ||||||
| if (current !== null) { | ||||||
| const prevProps = current.memoizedProps; | ||||||
| const prevState = current.memoizedState; | ||||||
| const instance = finishedWork.stateNode; | ||||||
| // We could update instance props and state here, | ||||||
| // but instead we rely on them being set during last render. | ||||||
| // TODO: revisit this when we implement resuming. | ||||||
| if (__DEV__) { | ||||||
| if ( | ||||||
| finishedWork.type === finishedWork.elementType && | ||||||
| !didWarnAboutReassigningProps | ||||||
| ) { | ||||||
| if (instance.props !== finishedWork.memoizedProps) { | ||||||
| console.error( | ||||||
| 'Expected %s props to match memoized props before ' + | ||||||
| 'getSnapshotBeforeUpdate. ' + | ||||||
| 'This might either be because of a bug in React, or because ' + | ||||||
| 'a component reassigns its own `this.props`. ' + | ||||||
| 'Please file an issue.', | ||||||
| getComponentNameFromFiber(finishedWork) || 'instance', | ||||||
| ); | ||||||
| } | ||||||
| if (instance.state !== finishedWork.memoizedState) { | ||||||
| console.error( | ||||||
| 'Expected %s state to match memoized state before ' + | ||||||
| 'getSnapshotBeforeUpdate. ' + | ||||||
| 'This might either be because of a bug in React, or because ' + | ||||||
| 'a component reassigns its own `this.state`. ' + | ||||||
| 'Please file an issue.', | ||||||
| getComponentNameFromFiber(finishedWork) || 'instance', | ||||||
| ); | ||||||
| } | ||||||
| case ClassComponent: { | ||||||
| const current = finishedWork.alternate; | ||||||
| if (current !== null) { | ||||||
| const prevProps = current.memoizedProps; | ||||||
| const prevState = current.memoizedState; | ||||||
| const instance = finishedWork.stateNode; | ||||||
| // We could update instance props and state here, | ||||||
| // but instead we rely on them being set during last render. | ||||||
| // TODO: revisit this when we implement resuming. | ||||||
| if (__DEV__) { | ||||||
| if ( | ||||||
| finishedWork.type === finishedWork.elementType && | ||||||
| !didWarnAboutReassigningProps | ||||||
| ) { | ||||||
| if (instance.props !== finishedWork.memoizedProps) { | ||||||
| console.error( | ||||||
| 'Expected %s props to match memoized props before ' + | ||||||
| 'getSnapshotBeforeUpdate. ' + | ||||||
| 'This might either be because of a bug in React, or because ' + | ||||||
| 'a component reassigns its own `this.props`. ' + | ||||||
| 'Please file an issue.', | ||||||
| getComponentNameFromFiber(finishedWork) || 'instance', | ||||||
| ); | ||||||
| } | ||||||
| } | ||||||
| const snapshot = instance.getSnapshotBeforeUpdate( | ||||||
| finishedWork.elementType === finishedWork.type | ||||||
| ? prevProps | ||||||
| : resolveDefaultProps(finishedWork.type, prevProps), | ||||||
| prevState, | ||||||
| ); | ||||||
| if (__DEV__) { | ||||||
| const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>); | ||||||
| if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) { | ||||||
| didWarnSet.add(finishedWork.type); | ||||||
| if (instance.state !== finishedWork.memoizedState) { | ||||||
| console.error( | ||||||
| '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' + | ||||||
| 'must be returned. You have returned undefined.', | ||||||
| getComponentNameFromFiber(finishedWork), | ||||||
| 'Expected %s state to match memoized state before ' + | ||||||
| 'getSnapshotBeforeUpdate. ' + | ||||||
| 'This might either be because of a bug in React, or because ' + | ||||||
| 'a component reassigns its own `this.state`. ' + | ||||||
| 'Please file an issue.', | ||||||
| getComponentNameFromFiber(finishedWork) || 'instance', | ||||||
| ); | ||||||
| } | ||||||
| } | ||||||
| instance.__reactInternalSnapshotBeforeUpdate = snapshot; | ||||||
| } | ||||||
| break; | ||||||
| } | ||||||
| case HostRoot: { | ||||||
| if (supportsMutation) { | ||||||
| const root = finishedWork.stateNode; | ||||||
| clearContainer(root.containerInfo); | ||||||
| const snapshot = instance.getSnapshotBeforeUpdate( | ||||||
| finishedWork.elementType === finishedWork.type | ||||||
| ? prevProps | ||||||
| : resolveDefaultProps(finishedWork.type, prevProps), | ||||||
| prevState, | ||||||
| ); | ||||||
| if (__DEV__) { | ||||||
| const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>); | ||||||
| if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) { | ||||||
| didWarnSet.add(finishedWork.type); | ||||||
| console.error( | ||||||
| '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' + | ||||||
| 'must be returned. You have returned undefined.', | ||||||
| getComponentNameFromFiber(finishedWork), | ||||||
| ); | ||||||
| } | ||||||
| } | ||||||
| break; | ||||||
| instance.__reactInternalSnapshotBeforeUpdate = snapshot; | ||||||
| } | ||||||
| case HostComponent: | ||||||
| case HostText: | ||||||
| case HostPortal: | ||||||
| case IncompleteClassComponent: | ||||||
| // Nothing to do for these component types | ||||||
| break; | ||||||
| default: { | ||||||
| invariant( | ||||||
| false, | ||||||
| 'This unit of work tag should not have side-effects. This error is ' + | ||||||
| 'likely caused by a bug in React. Please file an issue.', | ||||||
| ); | ||||||
| break; | ||||||
| } | ||||||
| case HostRoot: { | ||||||
| if (supportsMutation) { | ||||||
| const root = finishedWork.stateNode; | ||||||
| clearContainer(root.containerInfo); | ||||||
| } | ||||||
| break; | ||||||
| } | ||||||
| case HostComponent: | ||||||
| case HostText: | ||||||
| case HostPortal: | ||||||
| case IncompleteClassComponent: | ||||||
| // Nothing to do for these component types | ||||||
| break; | ||||||
| default: { | ||||||
| invariant( | ||||||
| false, | ||||||
| 'This unit of work tag should not have side-effects. This error is ' + | ||||||
| 'likely caused by a bug in React. Please file an issue.', | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| resetCurrentDebugFiberInDEV(); | ||||||
| } | ||||||
|
|
||||||
| resetCurrentDebugFiberInDEV(); | ||||||
| } | ||||||
|
|
||||||
| function commitBeforeMutationEffectsDeletion(deletion: Fiber) { | ||||||
| // TODO (effects) It would be nice to avoid calling doesFiberContain() | ||||||
| // Maybe we can repurpose one of the subtreeFlags positions for this instead? | ||||||
| // Use it to store which part of the tree the focused instance is in? | ||||||
| // This assumes we can safely determine that instance during the "render" phase. | ||||||
| if (doesFiberContain(deletion, ((focusedInstanceHandle: any): Fiber))) { | ||||||
| shouldFireAfterActiveInstanceBlur = true; | ||||||
| beforeActiveInstanceBlur(deletion); | ||||||
| if (enableCreateEventHandleAPI) { | ||||||
| // TODO (effects) It would be nice to avoid calling doesFiberContain() | ||||||
| // Maybe we can repurpose one of the subtreeFlags positions for this instead? | ||||||
| // Use it to store which part of the tree the focused instance is in? | ||||||
| // This assumes we can safely determine that instance during the "render" phase. | ||||||
| if (doesFiberContain(deletion, ((focusedInstanceHandle: any): Fiber))) { | ||||||
| shouldFireAfterActiveInstanceBlur = true; | ||||||
| beforeActiveInstanceBlur(deletion); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -2178,25 +2190,27 @@ function commitMutationEffects_begin(root: FiberRoot) { | |||||
| function commitMutationEffects_complete(root: FiberRoot) { | ||||||
| while (nextEffect !== null) { | ||||||
| const fiber = nextEffect; | ||||||
| if (__DEV__) { | ||||||
| setCurrentDebugFiberInDEV(fiber); | ||||||
| invokeGuardedCallback( | ||||||
| null, | ||||||
| commitMutationEffectsOnFiber, | ||||||
| null, | ||||||
| fiber, | ||||||
| root, | ||||||
| ); | ||||||
| if (hasCaughtError()) { | ||||||
| const error = clearCaughtError(); | ||||||
| captureCommitPhaseError(fiber, fiber.return, error); | ||||||
| } | ||||||
| resetCurrentDebugFiberInDEV(); | ||||||
| } else { | ||||||
| try { | ||||||
| commitMutationEffectsOnFiber(fiber, root); | ||||||
| } catch (error) { | ||||||
| captureCommitPhaseError(fiber, fiber.return, error); | ||||||
| if (fiber.flags !== NoFlags) { | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| if (__DEV__) { | ||||||
| setCurrentDebugFiberInDEV(fiber); | ||||||
| invokeGuardedCallback( | ||||||
| null, | ||||||
| commitMutationEffectsOnFiber, | ||||||
| null, | ||||||
| fiber, | ||||||
| root, | ||||||
| ); | ||||||
| if (hasCaughtError()) { | ||||||
| const error = clearCaughtError(); | ||||||
| captureCommitPhaseError(fiber, fiber.return, error); | ||||||
| } | ||||||
| resetCurrentDebugFiberInDEV(); | ||||||
| } else { | ||||||
| try { | ||||||
| commitMutationEffectsOnFiber(fiber, root); | ||||||
| } catch (error) { | ||||||
| captureCommitPhaseError(fiber, fiber.return, error); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 don't think I like the removal of the
Snapshotflag check around the switch statement. It seems a little weird to call a method that does not sound (but happens to be ) snapshot-specific (commitBeforeMutationEffectsOnFiber) and then do work without checking in that method.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.
Can we add the outer check (so we don't do the guarded callback unnecessarily) and leave the inner check?
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 was looking at
commitPassiveMountOnFiberwhich has the check outside. Overall I think there's some tension in this code between having generic naming and avoiding too many guards. I don't know what's a great way to balance but repeating the same check twice in a hot path also seems non-ideal?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.
Agreed. My initial impression was that moving the check outside was not great (b'c less clear) but I see what you're saying too. I don't have strong feelings.
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.
Most fibers do not have a Snapshot flag set. It's a rare case. So with the guard, the most common case is a single check, then you bail out. Without the guard, you have to do at least one check (the
switchcase) then additional checks for some branches (like class components).So I would add back the Snapshot check.
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 there's a check in the outer function. Nvm, didn't see that part.