mirror of
https://github.com/facebook/react.git
synced 2026-02-26 07:25:30 +00:00
Notify FragmentInstance of added/removed text (#35637)
Follow up to https://github.com/facebook/react/pull/35630 We don't currently have any operations that depend on the updating of text nodes added or removed after Fragment mount. But for the sake of completeness and extending the ability to any other host configs, this change calls `commitNewChildToFragmentInstance` and `deleteChildFromFragmentInstance` on HostText fibers. Both DOM and Fabric configs early return because we cannot attach event listeners or observers to text. In the future, there could be some stateful Fragment feature that uses text that could extend this.
This commit is contained in:
@@ -3544,40 +3544,48 @@ export function updateFragmentInstanceFiber(
|
||||
}
|
||||
|
||||
export function commitNewChildToFragmentInstance(
|
||||
childInstance: InstanceWithFragmentHandles,
|
||||
childInstance: InstanceWithFragmentHandles | Text,
|
||||
fragmentInstance: FragmentInstanceType,
|
||||
): void {
|
||||
if (childInstance.nodeType === TEXT_NODE) {
|
||||
return;
|
||||
}
|
||||
const instance: InstanceWithFragmentHandles = (childInstance: any);
|
||||
const eventListeners = fragmentInstance._eventListeners;
|
||||
if (eventListeners !== null) {
|
||||
for (let i = 0; i < eventListeners.length; i++) {
|
||||
const {type, listener, optionsOrUseCapture} = eventListeners[i];
|
||||
childInstance.addEventListener(type, listener, optionsOrUseCapture);
|
||||
instance.addEventListener(type, listener, optionsOrUseCapture);
|
||||
}
|
||||
}
|
||||
if (fragmentInstance._observers !== null) {
|
||||
fragmentInstance._observers.forEach(observer => {
|
||||
observer.observe(childInstance);
|
||||
observer.observe(instance);
|
||||
});
|
||||
}
|
||||
if (enableFragmentRefsInstanceHandles) {
|
||||
addFragmentHandleToInstance(childInstance, fragmentInstance);
|
||||
addFragmentHandleToInstance(instance, fragmentInstance);
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteChildFromFragmentInstance(
|
||||
childInstance: InstanceWithFragmentHandles,
|
||||
childInstance: InstanceWithFragmentHandles | Text,
|
||||
fragmentInstance: FragmentInstanceType,
|
||||
): void {
|
||||
if (childInstance.nodeType === TEXT_NODE) {
|
||||
return;
|
||||
}
|
||||
const instance: InstanceWithFragmentHandles = (childInstance: any);
|
||||
const eventListeners = fragmentInstance._eventListeners;
|
||||
if (eventListeners !== null) {
|
||||
for (let i = 0; i < eventListeners.length; i++) {
|
||||
const {type, listener, optionsOrUseCapture} = eventListeners[i];
|
||||
childInstance.removeEventListener(type, listener, optionsOrUseCapture);
|
||||
instance.removeEventListener(type, listener, optionsOrUseCapture);
|
||||
}
|
||||
}
|
||||
if (enableFragmentRefsInstanceHandles) {
|
||||
if (childInstance.unstable_reactFragments != null) {
|
||||
childInstance.unstable_reactFragments.delete(fragmentInstance);
|
||||
if (instance.unstable_reactFragments != null) {
|
||||
instance.unstable_reactFragments.delete(fragmentInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,10 @@ import {
|
||||
type PublicTextInstance,
|
||||
type PublicRootInstance,
|
||||
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
||||
import {enableFragmentRefsInstanceHandles} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
enableFragmentRefsInstanceHandles,
|
||||
enableFragmentRefsTextNodes,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
const {
|
||||
createNode,
|
||||
@@ -847,10 +850,15 @@ export function updateFragmentInstanceFiber(
|
||||
}
|
||||
|
||||
export function commitNewChildToFragmentInstance(
|
||||
childInstance: Instance,
|
||||
childInstance: Instance | TextInstance,
|
||||
fragmentInstance: FragmentInstanceType,
|
||||
): void {
|
||||
const publicInstance = getPublicInstance(childInstance);
|
||||
// Text nodes are not observable
|
||||
if (enableFragmentRefsTextNodes && childInstance.canonical == null) {
|
||||
return;
|
||||
}
|
||||
const instance: Instance = (childInstance: any);
|
||||
const publicInstance = getPublicInstance(instance);
|
||||
if (fragmentInstance._observers !== null) {
|
||||
if (publicInstance == null) {
|
||||
throw new Error('Expected to find a host node. This is a bug in React.');
|
||||
@@ -869,11 +877,16 @@ export function commitNewChildToFragmentInstance(
|
||||
}
|
||||
|
||||
export function deleteChildFromFragmentInstance(
|
||||
childInstance: Instance,
|
||||
childInstance: Instance | TextInstance,
|
||||
fragmentInstance: FragmentInstanceType,
|
||||
): void {
|
||||
// Text nodes are not observable
|
||||
if (enableFragmentRefsTextNodes && childInstance.canonical == null) {
|
||||
return;
|
||||
}
|
||||
const instance: Instance = (childInstance: any);
|
||||
const publicInstance = ((getPublicInstance(
|
||||
childInstance,
|
||||
instance,
|
||||
): any): PublicInstanceWithFragmentHandles);
|
||||
if (enableFragmentRefsInstanceHandles) {
|
||||
if (publicInstance.unstable_reactFragments != null) {
|
||||
|
||||
@@ -64,7 +64,10 @@ import {captureCommitPhaseError} from './ReactFiberWorkLoop';
|
||||
import {trackHostMutation} from './ReactFiberMutationTracking';
|
||||
|
||||
import {runWithFiberInDEV} from './ReactCurrentFiber';
|
||||
import {enableFragmentRefs} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
enableFragmentRefs,
|
||||
enableFragmentRefsTextNodes,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
export function commitHostMount(finishedWork: Fiber) {
|
||||
const type = finishedWork.type;
|
||||
@@ -258,7 +261,8 @@ export function commitNewChildToFragmentInstances(
|
||||
parentFragmentInstances: null | Array<FragmentInstanceType>,
|
||||
): void {
|
||||
if (
|
||||
fiber.tag !== HostComponent ||
|
||||
(fiber.tag !== HostComponent &&
|
||||
!(enableFragmentRefsTextNodes && fiber.tag === HostText)) ||
|
||||
// Only run fragment insertion effects for initial insertions
|
||||
fiber.alternate !== null ||
|
||||
parentFragmentInstances === null
|
||||
|
||||
@@ -62,6 +62,7 @@ import {
|
||||
enableFragmentRefs,
|
||||
enableEagerAlternateStateNodeCleanup,
|
||||
enableDefaultTransitionIndicator,
|
||||
enableFragmentRefsTextNodes,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
FunctionComponent,
|
||||
@@ -1533,7 +1534,11 @@ function commitDeletionEffectsOnFiber(
|
||||
if (!offscreenSubtreeWasHidden) {
|
||||
safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
||||
}
|
||||
if (enableFragmentRefs && deletedFiber.tag === HostComponent) {
|
||||
if (
|
||||
enableFragmentRefs &&
|
||||
(deletedFiber.tag === HostComponent ||
|
||||
(enableFragmentRefsTextNodes && deletedFiber.tag === HostText))
|
||||
) {
|
||||
commitFragmentInstanceDeletionEffects(deletedFiber);
|
||||
}
|
||||
// Intentional fallthrough to next branch
|
||||
@@ -3028,7 +3033,11 @@ export function disappearLayoutEffects(finishedWork: Fiber) {
|
||||
// TODO (Offscreen) Check: flags & RefStatic
|
||||
safelyDetachRef(finishedWork, finishedWork.return);
|
||||
|
||||
if (enableFragmentRefs && finishedWork.tag === HostComponent) {
|
||||
if (
|
||||
enableFragmentRefs &&
|
||||
(finishedWork.tag === HostComponent ||
|
||||
(enableFragmentRefsTextNodes && finishedWork.tag === HostText))
|
||||
) {
|
||||
commitFragmentInstanceDeletionEffects(finishedWork);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user