-
Notifications
You must be signed in to change notification settings - Fork 47k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move host effects to ReactFiberCommitHostEffects
- Loading branch information
1 parent
03e124c
commit d0c1936
Showing
2 changed files
with
421 additions
and
312 deletions.
There are no files selected for viewing
387 changes: 387 additions & 0 deletions
387
packages/react-reconciler/src/ReactFiberCommitHostEffects.js
This file contains 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 |
---|---|---|
@@ -0,0 +1,387 @@ | ||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @flow | ||
*/ | ||
|
||
import type { | ||
Instance, | ||
TextInstance, | ||
SuspenseInstance, | ||
Container, | ||
ChildSet, | ||
} from './ReactFiberConfig'; | ||
import type {Fiber, FiberRoot} from './ReactInternalTypes'; | ||
|
||
import { | ||
HostRoot, | ||
HostComponent, | ||
HostHoistable, | ||
HostSingleton, | ||
HostText, | ||
HostPortal, | ||
DehydratedFragment, | ||
} from './ReactWorkTags'; | ||
import {ContentReset, Placement} from './ReactFiberFlags'; | ||
import { | ||
supportsMutation, | ||
supportsResources, | ||
supportsSingletons, | ||
commitMount, | ||
commitUpdate, | ||
resetTextContent, | ||
commitTextUpdate, | ||
appendChild, | ||
appendChildToContainer, | ||
insertBefore, | ||
insertInContainerBefore, | ||
replaceContainerChildren, | ||
hideInstance, | ||
hideTextInstance, | ||
unhideInstance, | ||
unhideTextInstance, | ||
commitHydratedContainer, | ||
commitHydratedSuspenseInstance, | ||
} from './ReactFiberConfig'; | ||
import {captureCommitPhaseError} from './ReactFiberWorkLoop'; | ||
|
||
export function commitHostMount(finishedWork: Fiber) { | ||
const type = finishedWork.type; | ||
const props = finishedWork.memoizedProps; | ||
const instance: Instance = finishedWork.stateNode; | ||
try { | ||
commitMount(instance, type, props, finishedWork); | ||
} catch (error) { | ||
captureCommitPhaseError(finishedWork, finishedWork.return, error); | ||
} | ||
} | ||
|
||
export function commitHostUpdate( | ||
finishedWork: Fiber, | ||
newProps: any, | ||
oldProps: any, | ||
) { | ||
try { | ||
commitUpdate( | ||
finishedWork.stateNode, | ||
finishedWork.type, | ||
oldProps, | ||
newProps, | ||
finishedWork, | ||
); | ||
} catch (error) { | ||
captureCommitPhaseError(finishedWork, finishedWork.return, error); | ||
} | ||
} | ||
|
||
export function commitHostTextUpdate( | ||
finishedWork: Fiber, | ||
newText: string, | ||
oldText: string, | ||
) { | ||
const textInstance: TextInstance = finishedWork.stateNode; | ||
try { | ||
commitTextUpdate(textInstance, oldText, newText); | ||
} catch (error) { | ||
captureCommitPhaseError(finishedWork, finishedWork.return, error); | ||
} | ||
} | ||
|
||
export function commitHostResetTextContent(finishedWork: Fiber) { | ||
const instance: Instance = finishedWork.stateNode; | ||
try { | ||
resetTextContent(instance); | ||
} catch (error) { | ||
captureCommitPhaseError(finishedWork, finishedWork.return, error); | ||
} | ||
} | ||
|
||
export function commitShowHideHostInstance(node: Fiber, isHidden: boolean) { | ||
try { | ||
const instance = node.stateNode; | ||
if (isHidden) { | ||
hideInstance(instance); | ||
} else { | ||
unhideInstance(node.stateNode, node.memoizedProps); | ||
} | ||
} catch (error) { | ||
captureCommitPhaseError(node, node.return, error); | ||
} | ||
} | ||
|
||
export function commitShowHideHostTextInstance(node: Fiber, isHidden: boolean) { | ||
try { | ||
const instance = node.stateNode; | ||
if (isHidden) { | ||
hideTextInstance(instance); | ||
} else { | ||
unhideTextInstance(instance, node.memoizedProps); | ||
} | ||
} catch (error) { | ||
captureCommitPhaseError(node, node.return, error); | ||
} | ||
} | ||
|
||
function getHostParentFiber(fiber: Fiber): Fiber { | ||
let parent = fiber.return; | ||
while (parent !== null) { | ||
if (isHostParent(parent)) { | ||
return parent; | ||
} | ||
parent = parent.return; | ||
} | ||
|
||
throw new Error( | ||
'Expected to find a host parent. This error is likely caused by a bug ' + | ||
'in React. Please file an issue.', | ||
); | ||
} | ||
|
||
function isHostParent(fiber: Fiber): boolean { | ||
return ( | ||
fiber.tag === HostComponent || | ||
fiber.tag === HostRoot || | ||
(supportsResources ? fiber.tag === HostHoistable : false) || | ||
(supportsSingletons ? fiber.tag === HostSingleton : false) || | ||
fiber.tag === HostPortal | ||
); | ||
} | ||
|
||
function getHostSibling(fiber: Fiber): ?Instance { | ||
// We're going to search forward into the tree until we find a sibling host | ||
// node. Unfortunately, if multiple insertions are done in a row we have to | ||
// search past them. This leads to exponential search for the next sibling. | ||
// TODO: Find a more efficient way to do this. | ||
let node: Fiber = fiber; | ||
siblings: while (true) { | ||
// If we didn't find anything, let's try the next sibling. | ||
while (node.sibling === null) { | ||
if (node.return === null || isHostParent(node.return)) { | ||
// If we pop out of the root or hit the parent the fiber we are the | ||
// last sibling. | ||
return null; | ||
} | ||
// $FlowFixMe[incompatible-type] found when upgrading Flow | ||
node = node.return; | ||
} | ||
node.sibling.return = node.return; | ||
node = node.sibling; | ||
while ( | ||
node.tag !== HostComponent && | ||
node.tag !== HostText && | ||
(!supportsSingletons ? true : node.tag !== HostSingleton) && | ||
node.tag !== DehydratedFragment | ||
) { | ||
// If it is not host node and, we might have a host node inside it. | ||
// Try to search down until we find one. | ||
if (node.flags & Placement) { | ||
// If we don't have a child, try the siblings instead. | ||
continue siblings; | ||
} | ||
// If we don't have a child, try the siblings instead. | ||
// We also skip portals because they are not part of this host tree. | ||
if (node.child === null || node.tag === HostPortal) { | ||
continue siblings; | ||
} else { | ||
node.child.return = node; | ||
node = node.child; | ||
} | ||
} | ||
// Check if this host node is stable or about to be placed. | ||
if (!(node.flags & Placement)) { | ||
// Found it! | ||
return node.stateNode; | ||
} | ||
} | ||
} | ||
|
||
function insertOrAppendPlacementNodeIntoContainer( | ||
node: Fiber, | ||
before: ?Instance, | ||
parent: Container, | ||
): void { | ||
const {tag} = node; | ||
const isHost = tag === HostComponent || tag === HostText; | ||
if (isHost) { | ||
const stateNode = node.stateNode; | ||
if (before) { | ||
insertInContainerBefore(parent, stateNode, before); | ||
} else { | ||
appendChildToContainer(parent, stateNode); | ||
} | ||
} else if ( | ||
tag === HostPortal || | ||
(supportsSingletons ? tag === HostSingleton : false) | ||
) { | ||
// If the insertion itself is a portal, then we don't want to traverse | ||
// down its children. Instead, we'll get insertions from each child in | ||
// the portal directly. | ||
// If the insertion is a HostSingleton then it will be placed independently | ||
} else { | ||
const child = node.child; | ||
if (child !== null) { | ||
insertOrAppendPlacementNodeIntoContainer(child, before, parent); | ||
let sibling = child.sibling; | ||
while (sibling !== null) { | ||
insertOrAppendPlacementNodeIntoContainer(sibling, before, parent); | ||
sibling = sibling.sibling; | ||
} | ||
} | ||
} | ||
} | ||
|
||
function insertOrAppendPlacementNode( | ||
node: Fiber, | ||
before: ?Instance, | ||
parent: Instance, | ||
): void { | ||
const {tag} = node; | ||
const isHost = tag === HostComponent || tag === HostText; | ||
if (isHost) { | ||
const stateNode = node.stateNode; | ||
if (before) { | ||
insertBefore(parent, stateNode, before); | ||
} else { | ||
appendChild(parent, stateNode); | ||
} | ||
} else if ( | ||
tag === HostPortal || | ||
(supportsSingletons ? tag === HostSingleton : false) | ||
) { | ||
// If the insertion itself is a portal, then we don't want to traverse | ||
// down its children. Instead, we'll get insertions from each child in | ||
// the portal directly. | ||
// If the insertion is a HostSingleton then it will be placed independently | ||
} else { | ||
const child = node.child; | ||
if (child !== null) { | ||
insertOrAppendPlacementNode(child, before, parent); | ||
let sibling = child.sibling; | ||
while (sibling !== null) { | ||
insertOrAppendPlacementNode(sibling, before, parent); | ||
sibling = sibling.sibling; | ||
} | ||
} | ||
} | ||
} | ||
|
||
function commitPlacement(finishedWork: Fiber): void { | ||
if (!supportsMutation) { | ||
return; | ||
} | ||
|
||
if (supportsSingletons) { | ||
if (finishedWork.tag === HostSingleton) { | ||
// Singletons are already in the Host and don't need to be placed | ||
// Since they operate somewhat like Portals though their children will | ||
// have Placement and will get placed inside them | ||
return; | ||
} | ||
} | ||
// Recursively insert all host nodes into the parent. | ||
const parentFiber = getHostParentFiber(finishedWork); | ||
|
||
switch (parentFiber.tag) { | ||
case HostSingleton: { | ||
if (supportsSingletons) { | ||
const parent: Instance = parentFiber.stateNode; | ||
const before = getHostSibling(finishedWork); | ||
// We only have the top Fiber that was inserted but we need to recurse down its | ||
// children to find all the terminal nodes. | ||
insertOrAppendPlacementNode(finishedWork, before, parent); | ||
break; | ||
} | ||
// Fall through | ||
} | ||
case HostComponent: { | ||
const parent: Instance = parentFiber.stateNode; | ||
if (parentFiber.flags & ContentReset) { | ||
// Reset the text content of the parent before doing any insertions | ||
resetTextContent(parent); | ||
// Clear ContentReset from the effect tag | ||
parentFiber.flags &= ~ContentReset; | ||
} | ||
|
||
const before = getHostSibling(finishedWork); | ||
// We only have the top Fiber that was inserted but we need to recurse down its | ||
// children to find all the terminal nodes. | ||
insertOrAppendPlacementNode(finishedWork, before, parent); | ||
break; | ||
} | ||
case HostRoot: | ||
case HostPortal: { | ||
const parent: Container = parentFiber.stateNode.containerInfo; | ||
const before = getHostSibling(finishedWork); | ||
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent); | ||
break; | ||
} | ||
default: | ||
throw new Error( | ||
'Invalid host parent fiber. This error is likely caused by a bug ' + | ||
'in React. Please file an issue.', | ||
); | ||
} | ||
} | ||
|
||
export function commitHostPlacement(finishedWork: Fiber) { | ||
try { | ||
commitPlacement(finishedWork); | ||
} catch (error) { | ||
captureCommitPhaseError(finishedWork, finishedWork.return, error); | ||
} | ||
} | ||
|
||
export function commitHostRootContainerChildren( | ||
root: FiberRoot, | ||
finishedWork: Fiber, | ||
) { | ||
const containerInfo = root.containerInfo; | ||
const pendingChildren = root.pendingChildren; | ||
try { | ||
replaceContainerChildren(containerInfo, pendingChildren); | ||
} catch (error) { | ||
captureCommitPhaseError(finishedWork, finishedWork.return, error); | ||
} | ||
} | ||
|
||
export function commitHostPortalContainerChildren( | ||
portal: { | ||
containerInfo: Container, | ||
pendingChildren: ChildSet, | ||
... | ||
}, | ||
finishedWork: Fiber, | ||
) { | ||
const containerInfo = portal.containerInfo; | ||
const pendingChildren = portal.pendingChildren; | ||
try { | ||
replaceContainerChildren(containerInfo, pendingChildren); | ||
} catch (error) { | ||
captureCommitPhaseError(finishedWork, finishedWork.return, error); | ||
} | ||
} | ||
|
||
export function commitHostHydratedContainer( | ||
root: FiberRoot, | ||
finishedWork: Fiber, | ||
) { | ||
try { | ||
commitHydratedContainer(root.containerInfo); | ||
} catch (error) { | ||
captureCommitPhaseError(finishedWork, finishedWork.return, error); | ||
} | ||
} | ||
|
||
export function commitHostHydratedSuspense( | ||
suspenseInstance: SuspenseInstance, | ||
finishedWork: Fiber, | ||
) { | ||
try { | ||
commitHydratedSuspenseInstance(suspenseInstance); | ||
} catch (error) { | ||
captureCommitPhaseError(finishedWork, finishedWork.return, error); | ||
} | ||
} |
Oops, something went wrong.