Skip to content

Commit ce4b3e9

Browse files
authored
[react-interactions] Add optional searchNodes to Scope.queryAllNodes (#17293)
1 parent dee0304 commit ce4b3e9

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed

packages/react-reconciler/src/ReactFiberScope.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,17 @@ function collectScopedNodes(
3737
node: Fiber,
3838
fn: (type: string | Object, props: Object) => boolean,
3939
scopedNodes: Array<any>,
40+
searchNode: null | Set<Object>,
4041
): void {
4142
if (enableScopeAPI) {
4243
if (node.tag === HostComponent) {
44+
const instance = getPublicInstance(node.stateNode);
4345
const {type, memoizedProps} = node;
44-
if (fn(type, memoizedProps || emptyObject) === true) {
45-
scopedNodes.push(getPublicInstance(node.stateNode));
46+
if (
47+
(searchNode !== null && searchNode.has(instance)) ||
48+
fn(type, memoizedProps || emptyObject) === true
49+
) {
50+
scopedNodes.push(instance);
4651
}
4752
}
4853
let child = node.child;
@@ -51,7 +56,7 @@ function collectScopedNodes(
5156
child = getSuspenseFallbackChild(node);
5257
}
5358
if (child !== null) {
54-
collectScopedNodesFromChildren(child, fn, scopedNodes);
59+
collectScopedNodesFromChildren(child, fn, scopedNodes, searchNode);
5560
}
5661
}
5762
}
@@ -83,10 +88,11 @@ function collectScopedNodesFromChildren(
8388
startingChild: Fiber,
8489
fn: (type: string | Object, props: Object) => boolean,
8590
scopedNodes: Array<any>,
91+
searchNode: null | Set<Object>,
8692
): void {
8793
let child = startingChild;
8894
while (child !== null) {
89-
collectScopedNodes(child, fn, scopedNodes);
95+
collectScopedNodes(child, fn, scopedNodes, searchNode);
9096
child = child.sibling;
9197
}
9298
}
@@ -192,12 +198,14 @@ export function createScopeMethods(
192198
},
193199
queryAllNodes(
194200
fn: (type: string | Object, props: Object) => boolean,
201+
searchNodes?: Array<Object>,
195202
): null | Array<Object> {
196203
const currentFiber = ((instance.fiber: any): Fiber);
197204
const child = currentFiber.child;
198205
const scopedNodes = [];
206+
const searchNodeSet = searchNodes ? new Set(searchNodes) : null;
199207
if (child !== null) {
200-
collectScopedNodesFromChildren(child, fn, scopedNodes);
208+
collectScopedNodesFromChildren(child, fn, scopedNodes, searchNodeSet);
201209
}
202210
return scopedNodes.length === 0 ? null : scopedNodes;
203211
},

packages/react-reconciler/src/__tests__/ReactScope-test.internal.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,50 @@ describe('ReactScope', () => {
7272
expect(scopeRef.current).toBe(null);
7373
});
7474

75+
it('queryAllNodes() works as intended with included nodes array', () => {
76+
const testScopeQuery = (type, props) => type === 'div';
77+
const TestScope = React.unstable_createScope();
78+
const scopeRef = React.createRef();
79+
const divRef = React.createRef();
80+
const spanRef = React.createRef();
81+
const aRef = React.createRef();
82+
83+
function Test({toggle}) {
84+
return toggle ? (
85+
<TestScope ref={scopeRef}>
86+
<div ref={divRef}>DIV</div>
87+
<span ref={spanRef}>SPAN</span>
88+
<a ref={aRef}>A</a>
89+
</TestScope>
90+
) : (
91+
<TestScope ref={scopeRef}>
92+
<a ref={aRef}>A</a>
93+
<div ref={divRef}>DIV</div>
94+
<span ref={spanRef}>SPAN</span>
95+
</TestScope>
96+
);
97+
}
98+
99+
ReactDOM.render(<Test toggle={true} />, container);
100+
let nodes = scopeRef.current.queryAllNodes(testScopeQuery);
101+
expect(nodes).toEqual([divRef.current]);
102+
nodes = scopeRef.current.queryAllNodes(testScopeQuery, [spanRef.current]);
103+
expect(nodes).toEqual([divRef.current, spanRef.current]);
104+
nodes = scopeRef.current.queryAllNodes(testScopeQuery, [
105+
spanRef.current,
106+
aRef.current,
107+
]);
108+
expect(nodes).toEqual([divRef.current, spanRef.current, aRef.current]);
109+
ReactDOM.render(<Test toggle={false} />, container);
110+
nodes = scopeRef.current.queryAllNodes(testScopeQuery, [
111+
spanRef.current,
112+
aRef.current,
113+
]);
114+
expect(nodes).toEqual([aRef.current, divRef.current, spanRef.current]);
115+
ReactDOM.render(null, container);
116+
expect(scopeRef.current).toBe(null);
117+
});
118+
75119
it('queryFirstNode() works as intended', () => {
76120
const testScopeQuery = (type, props) => true;
77121
const TestScope = React.unstable_createScope();

packages/shared/ReactTypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ export type ReactScopeMethods = {|
171171
getProps(): Object,
172172
queryAllNodes(
173173
(type: string | Object, props: Object) => boolean,
174+
searchNodes?: Array<Object>,
174175
): null | Array<Object>,
175176
queryFirstNode(
176177
(type: string | Object, props: Object) => boolean,

0 commit comments

Comments
 (0)