[react-interactions] Add optional searchNodes to Scope.queryAllNodes (#17293)
Dominic Gannaway committed
Nov 6, 2019 at 22:52 UTC
ce4b3e998191445b2934f6846ee7661578ef6444
3 files changed
+58
-5
packages/react-reconciler/src/ReactFiberScope.js
+13
-5
@@ -37,12 +37,17 @@ function collectScopedNodes(
37
node: Fiber,
38
fn: (type: string | Object, props: Object) => boolean,
39
scopedNodes: Array<any>,
40
+ searchNode: null | Set<Object>,
41
): void {
42
if (enableScopeAPI) {
43
if (node.tag === HostComponent) {
44
+ const instance = getPublicInstance(node.stateNode);
45
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);
51
}
52
}
53
let child = node.child;
@@ -51,7 +56,7 @@ function collectScopedNodes(
56
child = getSuspenseFallbackChild(node);
57
}
58
if (child !== null) {
54
- collectScopedNodesFromChildren(child, fn, scopedNodes);
59
+ collectScopedNodesFromChildren(child, fn, scopedNodes, searchNode);
60
}
61
}
62
}
@@ -83,10 +88,11 @@ function collectScopedNodesFromChildren(
88
startingChild: Fiber,
89
fn: (type: string | Object, props: Object) => boolean,
90
scopedNodes: Array<any>,
91
+ searchNode: null | Set<Object>,
92
): void {
93
let child = startingChild;
94
while (child !== null) {
89
- collectScopedNodes(child, fn, scopedNodes);
95
+ collectScopedNodes(child, fn, scopedNodes, searchNode);
96
child = child.sibling;
97
}
98
}
@@ -192,12 +198,14 @@ export function createScopeMethods(
198
},
199
queryAllNodes(
200
fn: (type: string | Object, props: Object) => boolean,
201
+ searchNodes?: Array<Object>,
202
): null | Array<Object> {
203
const currentFiber = ((instance.fiber: any): Fiber);
204
const child = currentFiber.child;
205
const scopedNodes = [];
206
+ const searchNodeSet = searchNodes ? new Set(searchNodes) : null;
207
if (child !== null) {
200
- collectScopedNodesFromChildren(child, fn, scopedNodes);
208
+ collectScopedNodesFromChildren(child, fn, scopedNodes, searchNodeSet);
209
}
210
return scopedNodes.length === 0 ? null : scopedNodes;
211
},
packages/react-reconciler/src/__tests__/ReactScope-test.internal.js
+44
@@ -72,6 +72,50 @@ describe('ReactScope', () => {
72
expect(scopeRef.current).toBe(null);
73
});
74
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
+
119
it('queryFirstNode() works as intended', () => {
120
const testScopeQuery = (type, props) => true;
121
const TestScope = React.unstable_createScope();
packages/shared/ReactTypes.js
+1
@@ -171,6 +171,7 @@ export type ReactScopeMethods = {|
171
getProps(): Object,
172
queryAllNodes(
173
(type: string | Object, props: Object) => boolean,
174
+ searchNodes?: Array<Object>,
175
): null | Array<Object>,
176
queryFirstNode(
177
(type: string | Object, props: Object) => boolean,