mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
209 lines
5.1 KiB
JavaScript
209 lines
5.1 KiB
JavaScript
/**
|
|
* 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 {Fiber} from './ReactInternalTypes';
|
|
import type {
|
|
ReactScopeInstance,
|
|
ReactContext,
|
|
ReactScopeQuery,
|
|
} from 'shared/ReactTypes';
|
|
|
|
import {
|
|
getPublicInstance,
|
|
getInstanceFromNode,
|
|
getInstanceFromScope,
|
|
} from './ReactFiberConfig';
|
|
import {isFiberSuspenseAndTimedOut} from './ReactFiberTreeReflection';
|
|
|
|
import {HostComponent, ScopeComponent, ContextProvider} from './ReactWorkTags';
|
|
import {enableScopeAPI} from 'shared/ReactFeatureFlags';
|
|
|
|
function getSuspenseFallbackChild(fiber: Fiber): Fiber | null {
|
|
return ((((fiber.child: any): Fiber).sibling: any): Fiber).child;
|
|
}
|
|
|
|
const emptyObject = {};
|
|
|
|
function collectScopedNodes(
|
|
node: Fiber,
|
|
fn: ReactScopeQuery,
|
|
scopedNodes: Array<any>,
|
|
): void {
|
|
if (enableScopeAPI) {
|
|
if (node.tag === HostComponent) {
|
|
const {type, memoizedProps, stateNode} = node;
|
|
const instance = getPublicInstance(stateNode);
|
|
if (
|
|
instance !== null &&
|
|
fn(type, memoizedProps || emptyObject, instance) === true
|
|
) {
|
|
scopedNodes.push(instance);
|
|
}
|
|
}
|
|
let child = node.child;
|
|
|
|
if (isFiberSuspenseAndTimedOut(node)) {
|
|
child = getSuspenseFallbackChild(node);
|
|
}
|
|
if (child !== null) {
|
|
collectScopedNodesFromChildren(child, fn, scopedNodes);
|
|
}
|
|
}
|
|
}
|
|
|
|
function collectFirstScopedNode(
|
|
node: Fiber,
|
|
fn: ReactScopeQuery,
|
|
): null | Object {
|
|
if (enableScopeAPI) {
|
|
if (node.tag === HostComponent) {
|
|
const {type, memoizedProps, stateNode} = node;
|
|
const instance = getPublicInstance(stateNode);
|
|
if (instance !== null && fn(type, memoizedProps, instance) === true) {
|
|
return instance;
|
|
}
|
|
}
|
|
let child = node.child;
|
|
|
|
if (isFiberSuspenseAndTimedOut(node)) {
|
|
child = getSuspenseFallbackChild(node);
|
|
}
|
|
if (child !== null) {
|
|
return collectFirstScopedNodeFromChildren(child, fn);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function collectScopedNodesFromChildren(
|
|
startingChild: Fiber,
|
|
fn: ReactScopeQuery,
|
|
scopedNodes: Array<any>,
|
|
): void {
|
|
let child: null | Fiber = startingChild;
|
|
while (child !== null) {
|
|
collectScopedNodes(child, fn, scopedNodes);
|
|
child = child.sibling;
|
|
}
|
|
}
|
|
|
|
function collectFirstScopedNodeFromChildren(
|
|
startingChild: Fiber,
|
|
fn: ReactScopeQuery,
|
|
): Object | null {
|
|
let child: null | Fiber = startingChild;
|
|
while (child !== null) {
|
|
const scopedNode = collectFirstScopedNode(child, fn);
|
|
if (scopedNode !== null) {
|
|
return scopedNode;
|
|
}
|
|
child = child.sibling;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function collectNearestContextValues<T>(
|
|
node: Fiber,
|
|
context: ReactContext<T>,
|
|
childContextValues: Array<T>,
|
|
): void {
|
|
if (node.tag === ContextProvider && node.type === context) {
|
|
const contextValue = node.memoizedProps.value;
|
|
childContextValues.push(contextValue);
|
|
} else {
|
|
let child = node.child;
|
|
|
|
if (isFiberSuspenseAndTimedOut(node)) {
|
|
child = getSuspenseFallbackChild(node);
|
|
}
|
|
if (child !== null) {
|
|
collectNearestChildContextValues(child, context, childContextValues);
|
|
}
|
|
}
|
|
}
|
|
|
|
function collectNearestChildContextValues<T>(
|
|
startingChild: Fiber | null,
|
|
context: ReactContext<T>,
|
|
childContextValues: Array<T>,
|
|
): void {
|
|
let child = startingChild;
|
|
while (child !== null) {
|
|
collectNearestContextValues(child, context, childContextValues);
|
|
child = child.sibling;
|
|
}
|
|
}
|
|
|
|
function DO_NOT_USE_queryAllNodes(
|
|
this: $FlowFixMe,
|
|
fn: ReactScopeQuery,
|
|
): null | Array<Object> {
|
|
const currentFiber = getInstanceFromScope(this);
|
|
if (currentFiber === null) {
|
|
return null;
|
|
}
|
|
const child = currentFiber.child;
|
|
const scopedNodes: Array<any> = [];
|
|
if (child !== null) {
|
|
collectScopedNodesFromChildren(child, fn, scopedNodes);
|
|
}
|
|
return scopedNodes.length === 0 ? null : scopedNodes;
|
|
}
|
|
|
|
function DO_NOT_USE_queryFirstNode(
|
|
this: $FlowFixMe,
|
|
fn: ReactScopeQuery,
|
|
): null | Object {
|
|
const currentFiber = getInstanceFromScope(this);
|
|
if (currentFiber === null) {
|
|
return null;
|
|
}
|
|
const child = currentFiber.child;
|
|
if (child !== null) {
|
|
return collectFirstScopedNodeFromChildren(child, fn);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function containsNode(this: $FlowFixMe, node: Object): boolean {
|
|
let fiber = getInstanceFromNode(node);
|
|
while (fiber !== null) {
|
|
if (fiber.tag === ScopeComponent && fiber.stateNode === this) {
|
|
return true;
|
|
}
|
|
fiber = fiber.return;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function getChildContextValues<T>(
|
|
this: $FlowFixMe,
|
|
context: ReactContext<T>,
|
|
): Array<T> {
|
|
const currentFiber = getInstanceFromScope(this);
|
|
if (currentFiber === null) {
|
|
return [];
|
|
}
|
|
const child = currentFiber.child;
|
|
const childContextValues: Array<T> = [];
|
|
if (child !== null) {
|
|
collectNearestChildContextValues(child, context, childContextValues);
|
|
}
|
|
return childContextValues;
|
|
}
|
|
|
|
export function createScopeInstance(): ReactScopeInstance {
|
|
return {
|
|
DO_NOT_USE_queryAllNodes,
|
|
DO_NOT_USE_queryFirstNode,
|
|
containsNode,
|
|
getChildContextValues,
|
|
};
|
|
}
|