mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[react-interactions] Adds more experimental Scope API methods (#17042)
This commit is contained in:
@@ -461,3 +461,7 @@ export function updateFundamentalComponent(fundamentalInstance) {
|
||||
export function unmountFundamentalComponent(fundamentalInstance) {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function getInstanceFromNode(node) {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
+9
-1
@@ -7,7 +7,11 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import {precacheFiberNode, updateFiberProps} from './ReactDOMComponentTree';
|
||||
import {
|
||||
precacheFiberNode,
|
||||
updateFiberProps,
|
||||
getClosestInstanceFromNode,
|
||||
} from './ReactDOMComponentTree';
|
||||
import {
|
||||
createElement,
|
||||
createTextNode,
|
||||
@@ -976,3 +980,7 @@ export function unmountFundamentalComponent(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getInstanceFromNode(node: HTMLElement): null | Object {
|
||||
return getClosestInstanceFromNode(node) || null;
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ function MyComponent(props) {
|
||||
);
|
||||
}
|
||||
|
||||
// Using the ref, we can get the host nodes via getScopedNodes()
|
||||
const divs = divOnlyScope.current.getScopedNodes();
|
||||
// Using the ref, we can get the host nodes via getAllNodes()
|
||||
const divs = divOnlyScope.current.getAllNodes();
|
||||
|
||||
// [<div>DIV 1</div>, <div>DIV 2</div>, <div>DIV 3</div>]
|
||||
console.log(divs);
|
||||
@@ -72,7 +72,17 @@ Returns the parent `ReactScopeInterface` of the scope node or `null` if none exi
|
||||
|
||||
Returns the current `props` object of the scope node.
|
||||
|
||||
### getScopedNodes: () => null | Array<HTMLElement>
|
||||
### getAllNodes: () => null | Array<HTMLElement>
|
||||
|
||||
Returns an array of all child host nodes that successfully match when queried using the
|
||||
query function passed to the scope. Returns `null` if there are no matching host nodes.
|
||||
query function passed to the scope. Returns `null` if there are no matching host nodes.
|
||||
|
||||
### getFirstNode: () => null | HTMLElement
|
||||
|
||||
Returns the first child host node that successfully matches when queried using the
|
||||
query function passed to the scope. Returns `null` if there is no matching host node.
|
||||
|
||||
### containsNode: (node: HTMLElement) => boolean
|
||||
|
||||
Returns `true` or `false` depending on if the given `HTMLElement` is a descendant
|
||||
of the scope's sub-tree.
|
||||
@@ -15,7 +15,7 @@ function FocusableNodeCollector(props) {
|
||||
const scope = scopeRef.current;
|
||||
|
||||
if (scope) {
|
||||
const tabFocusableNodes = scope.getScopedNodes();
|
||||
const tabFocusableNodes = scope.getAllNodes();
|
||||
if (tabFocusableNodes && props.onFocusableNodes) {
|
||||
props.onFocusableNodes(tabFocusableNodes);
|
||||
}
|
||||
|
||||
@@ -66,10 +66,14 @@ export default function FocusContain({
|
||||
useLayoutEffect(
|
||||
() => {
|
||||
const scope = scopeRef.current;
|
||||
if (scope !== null && disabled !== true) {
|
||||
const elems = scope.getScopedNodes();
|
||||
if (elems && elems.indexOf(document.activeElement) === -1) {
|
||||
elems[0].focus();
|
||||
if (
|
||||
scope !== null &&
|
||||
disabled !== true &&
|
||||
!scope.containsNode(document.activeElement)
|
||||
) {
|
||||
const fistElem = scope.getFirstNode();
|
||||
if (fistElem !== null) {
|
||||
fistElem.focus();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -30,9 +30,9 @@ type FocusGroupProps = {|
|
||||
const {useRef} = React;
|
||||
|
||||
function focusGroupItem(cell: ReactScopeMethods, event: KeyboardEvent): void {
|
||||
const tabbableNodes = cell.getScopedNodes();
|
||||
if (tabbableNodes !== null && tabbableNodes.length > 0) {
|
||||
tabbableNodes[0].focus();
|
||||
const firstScopedNode = cell.getFirstNode();
|
||||
if (firstScopedNode !== null) {
|
||||
firstScopedNode.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ export function createFocusGroup(
|
||||
const tabScope = getGroupProps(currentItem).tabScopeRef.current;
|
||||
if (tabScope) {
|
||||
const activeNode = document.activeElement;
|
||||
const nodes = tabScope.getScopedNodes();
|
||||
const nodes = tabScope.getAllNodes();
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
if (node !== activeNode) {
|
||||
|
||||
@@ -39,9 +39,9 @@ type FocusTableProps = {|
|
||||
const {useRef} = React;
|
||||
|
||||
function focusScope(cell: ReactScopeMethods, event?: KeyboardEvent): void {
|
||||
const tabbableNodes = cell.getScopedNodes();
|
||||
if (tabbableNodes !== null && tabbableNodes.length > 0) {
|
||||
tabbableNodes[0].focus();
|
||||
const firstScopedNode = cell.getFirstNode();
|
||||
if (firstScopedNode !== null) {
|
||||
firstScopedNode.focus();
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
@@ -209,7 +209,7 @@ export function createFocusTable(
|
||||
const tabScope = getTableProps(currentCell).tabScopeRef.current;
|
||||
if (tabScope) {
|
||||
const activeNode = document.activeElement;
|
||||
const nodes = tabScope.getScopedNodes();
|
||||
const nodes = tabScope.getAllNodes();
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
if (node !== activeNode) {
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@ describe('TabbableScope', () => {
|
||||
container = null;
|
||||
});
|
||||
|
||||
it('getScopedNodes() works as intended', () => {
|
||||
it('getAllNodes() works as intended', () => {
|
||||
const scopeRef = React.createRef();
|
||||
const nodeRefA = React.createRef();
|
||||
const nodeRefB = React.createRef();
|
||||
@@ -58,7 +58,7 @@ describe('TabbableScope', () => {
|
||||
}
|
||||
|
||||
ReactDOM.render(<Test />, container);
|
||||
let nodes = scopeRef.current.getScopedNodes();
|
||||
let nodes = scopeRef.current.getAllNodes();
|
||||
expect(nodes).toEqual([
|
||||
nodeRefA.current,
|
||||
nodeRefB.current,
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function getTabbableNodes(
|
||||
number,
|
||||
null | HTMLElement,
|
||||
] {
|
||||
const tabbableNodes = scope.getScopedNodes();
|
||||
const tabbableNodes = scope.getAllNodes();
|
||||
if (tabbableNodes === null || tabbableNodes.length === 0) {
|
||||
return [null, null, null, 0, null];
|
||||
}
|
||||
|
||||
@@ -491,3 +491,7 @@ export function unmountFundamentalComponent(fundamentalInstance) {
|
||||
export function cloneFundamentalInstance(fundamentalInstance) {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function getInstanceFromNode(node) {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
@@ -530,3 +530,7 @@ export function updateFundamentalComponent(fundamentalInstance) {
|
||||
export function unmountFundamentalComponent(fundamentalInstance) {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function getInstanceFromNode(node) {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
@@ -432,6 +432,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
|
||||
hidden: instance.hidden,
|
||||
};
|
||||
},
|
||||
|
||||
getInstanceFromNode() {
|
||||
throw new Error('Not yet implemented.');
|
||||
},
|
||||
};
|
||||
|
||||
const hostConfig = useMutation
|
||||
|
||||
+62
-2
@@ -14,7 +14,7 @@ import type {
|
||||
ReactScopeMethods,
|
||||
} from 'shared/ReactTypes';
|
||||
|
||||
import {getPublicInstance} from './ReactFiberHostConfig';
|
||||
import {getPublicInstance, getInstanceFromNode} from './ReactFiberHostConfig';
|
||||
|
||||
import {
|
||||
HostComponent,
|
||||
@@ -54,6 +54,29 @@ function collectScopedNodes(
|
||||
}
|
||||
}
|
||||
|
||||
function collectFirstScopedNode(
|
||||
node: Fiber,
|
||||
fn: (type: string | Object, props: Object) => boolean,
|
||||
): null | Object {
|
||||
if (enableScopeAPI) {
|
||||
if (node.tag === HostComponent) {
|
||||
const {type, memoizedProps} = node;
|
||||
if (fn(type, memoizedProps) === true) {
|
||||
return getPublicInstance(node.stateNode);
|
||||
}
|
||||
}
|
||||
let child = node.child;
|
||||
|
||||
if (isFiberSuspenseAndTimedOut(node)) {
|
||||
child = getSuspenseFallbackChild(node);
|
||||
}
|
||||
if (child !== null) {
|
||||
return collectFirstScopedNodeFromChildren(child, fn);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function collectScopedNodesFromChildren(
|
||||
startingChild: Fiber,
|
||||
fn: (type: string | Object, props: Object) => boolean,
|
||||
@@ -66,6 +89,21 @@ function collectScopedNodesFromChildren(
|
||||
}
|
||||
}
|
||||
|
||||
function collectFirstScopedNodeFromChildren(
|
||||
startingChild: Fiber,
|
||||
fn: (type: string | Object, props: Object) => boolean,
|
||||
): Object | null {
|
||||
let child = startingChild;
|
||||
while (child !== null) {
|
||||
const scopedNode = collectFirstScopedNode(child, fn);
|
||||
if (scopedNode !== null) {
|
||||
return scopedNode;
|
||||
}
|
||||
child = child.sibling;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function collectNearestScopeMethods(
|
||||
node: Fiber,
|
||||
scope: ReactScope,
|
||||
@@ -151,7 +189,7 @@ export function createScopeMethods(
|
||||
const currentFiber = ((instance.fiber: any): Fiber);
|
||||
return currentFiber.memoizedProps;
|
||||
},
|
||||
getScopedNodes(): null | Array<Object> {
|
||||
getAllNodes(): null | Array<Object> {
|
||||
const currentFiber = ((instance.fiber: any): Fiber);
|
||||
const child = currentFiber.child;
|
||||
const scopedNodes = [];
|
||||
@@ -160,5 +198,27 @@ export function createScopeMethods(
|
||||
}
|
||||
return scopedNodes.length === 0 ? null : scopedNodes;
|
||||
},
|
||||
getFirstNode(): null | Object {
|
||||
const currentFiber = ((instance.fiber: any): Fiber);
|
||||
const child = currentFiber.child;
|
||||
if (child !== null) {
|
||||
return collectFirstScopedNodeFromChildren(child, fn);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
containsNode(node: Object): boolean {
|
||||
let fiber = getInstanceFromNode(node);
|
||||
while (fiber !== null) {
|
||||
if (
|
||||
fiber.tag === ScopeComponent &&
|
||||
fiber.type === scope &&
|
||||
fiber.stateNode === instance
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
fiber = fiber.return;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ describe('ReactScope', () => {
|
||||
container = null;
|
||||
});
|
||||
|
||||
it('getScopedNodes() works as intended', () => {
|
||||
it('getAllNodes() works as intended', () => {
|
||||
const TestScope = React.unstable_createScope((type, props) => true);
|
||||
const scopeRef = React.createRef();
|
||||
const divRef = React.createRef();
|
||||
@@ -62,16 +62,98 @@ describe('ReactScope', () => {
|
||||
}
|
||||
|
||||
ReactDOM.render(<Test toggle={true} />, container);
|
||||
let nodes = scopeRef.current.getScopedNodes();
|
||||
let nodes = scopeRef.current.getAllNodes();
|
||||
expect(nodes).toEqual([divRef.current, spanRef.current, aRef.current]);
|
||||
ReactDOM.render(<Test toggle={false} />, container);
|
||||
nodes = scopeRef.current.getScopedNodes();
|
||||
nodes = scopeRef.current.getAllNodes();
|
||||
expect(nodes).toEqual([aRef.current, divRef.current, spanRef.current]);
|
||||
ReactDOM.render(null, container);
|
||||
expect(scopeRef.current).toBe(null);
|
||||
});
|
||||
|
||||
it('mixed getParent() and getScopedNodes() works as intended', () => {
|
||||
it('getFirstNode() works as intended', () => {
|
||||
const TestScope = React.unstable_createScope((type, props) => true);
|
||||
const scopeRef = React.createRef();
|
||||
const divRef = React.createRef();
|
||||
const spanRef = React.createRef();
|
||||
const aRef = React.createRef();
|
||||
|
||||
function Test({toggle}) {
|
||||
return toggle ? (
|
||||
<TestScope ref={scopeRef}>
|
||||
<div ref={divRef}>DIV</div>
|
||||
<span ref={spanRef}>SPAN</span>
|
||||
<a ref={aRef}>A</a>
|
||||
</TestScope>
|
||||
) : (
|
||||
<TestScope ref={scopeRef}>
|
||||
<a ref={aRef}>A</a>
|
||||
<div ref={divRef}>DIV</div>
|
||||
<span ref={spanRef}>SPAN</span>
|
||||
</TestScope>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.render(<Test toggle={true} />, container);
|
||||
let node = scopeRef.current.getFirstNode();
|
||||
expect(node).toEqual(divRef.current);
|
||||
ReactDOM.render(<Test toggle={false} />, container);
|
||||
node = scopeRef.current.getFirstNode();
|
||||
expect(node).toEqual(aRef.current);
|
||||
ReactDOM.render(null, container);
|
||||
expect(scopeRef.current).toBe(null);
|
||||
});
|
||||
|
||||
it('containsNode() works as intended', () => {
|
||||
const TestScope = React.unstable_createScope((type, props) => true);
|
||||
const scopeRef = React.createRef();
|
||||
const divRef = React.createRef();
|
||||
const spanRef = React.createRef();
|
||||
const aRef = React.createRef();
|
||||
const outerSpan = React.createRef();
|
||||
const emRef = React.createRef();
|
||||
|
||||
function Test({toggle}) {
|
||||
return toggle ? (
|
||||
<div>
|
||||
<span ref={outerSpan}>SPAN</span>
|
||||
<TestScope ref={scopeRef}>
|
||||
<div ref={divRef}>DIV</div>
|
||||
<span ref={spanRef}>SPAN</span>
|
||||
<a ref={aRef}>A</a>
|
||||
</TestScope>
|
||||
<em ref={emRef}>EM</em>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<TestScope ref={scopeRef}>
|
||||
<a ref={aRef}>A</a>
|
||||
<div ref={divRef}>DIV</div>
|
||||
<span ref={spanRef}>SPAN</span>
|
||||
<em ref={emRef}>EM</em>
|
||||
</TestScope>
|
||||
<span ref={outerSpan}>SPAN</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.render(<Test toggle={true} />, container);
|
||||
expect(scopeRef.current.containsNode(divRef.current)).toBe(true);
|
||||
expect(scopeRef.current.containsNode(spanRef.current)).toBe(true);
|
||||
expect(scopeRef.current.containsNode(aRef.current)).toBe(true);
|
||||
expect(scopeRef.current.containsNode(outerSpan.current)).toBe(false);
|
||||
expect(scopeRef.current.containsNode(emRef.current)).toBe(false);
|
||||
ReactDOM.render(<Test toggle={false} />, container);
|
||||
expect(scopeRef.current.containsNode(divRef.current)).toBe(true);
|
||||
expect(scopeRef.current.containsNode(spanRef.current)).toBe(true);
|
||||
expect(scopeRef.current.containsNode(aRef.current)).toBe(true);
|
||||
expect(scopeRef.current.containsNode(outerSpan.current)).toBe(false);
|
||||
expect(scopeRef.current.containsNode(emRef.current)).toBe(true);
|
||||
ReactDOM.render(<Test toggle={true} />, container);
|
||||
expect(scopeRef.current.containsNode(emRef.current)).toBe(false);
|
||||
});
|
||||
|
||||
it('mixed getParent() and getAllNodes() works as intended', () => {
|
||||
const TestScope = React.unstable_createScope((type, props) => true);
|
||||
const TestScope2 = React.unstable_createScope((type, props) => true);
|
||||
const refA = React.createRef();
|
||||
@@ -108,14 +190,14 @@ describe('ReactScope', () => {
|
||||
ReactDOM.render(<Test />, container);
|
||||
const dParent = refD.current.getParent();
|
||||
expect(dParent).not.toBe(null);
|
||||
expect(dParent.getScopedNodes()).toEqual([
|
||||
expect(dParent.getAllNodes()).toEqual([
|
||||
divA.current,
|
||||
spanB.current,
|
||||
divB.current,
|
||||
]);
|
||||
const cParent = refC.current.getParent();
|
||||
expect(cParent).not.toBe(null);
|
||||
expect(cParent.getScopedNodes()).toEqual([
|
||||
expect(cParent.getAllNodes()).toEqual([
|
||||
spanA.current,
|
||||
divA.current,
|
||||
spanB.current,
|
||||
@@ -196,7 +278,7 @@ describe('ReactScope', () => {
|
||||
);
|
||||
container.innerHTML = html;
|
||||
ReactDOM.hydrate(<Test />, container);
|
||||
const nodes = scopeRef.current.getScopedNodes();
|
||||
const nodes = scopeRef.current.getAllNodes();
|
||||
expect(nodes).toEqual([divRef.current, spanRef.current, aRef.current]);
|
||||
});
|
||||
|
||||
@@ -250,7 +332,7 @@ describe('ReactScope', () => {
|
||||
ReactTestRenderer = require('react-test-renderer');
|
||||
});
|
||||
|
||||
it('getScopedNodes() works as intended', () => {
|
||||
it('getAllNodes() works as intended', () => {
|
||||
const TestScope = React.unstable_createScope((type, props) => true);
|
||||
const scopeRef = React.createRef();
|
||||
const divRef = React.createRef();
|
||||
@@ -278,14 +360,49 @@ describe('ReactScope', () => {
|
||||
return element;
|
||||
},
|
||||
});
|
||||
let nodes = scopeRef.current.getScopedNodes();
|
||||
let nodes = scopeRef.current.getAllNodes();
|
||||
expect(nodes).toEqual([divRef.current, spanRef.current, aRef.current]);
|
||||
renderer.update(<Test toggle={false} />);
|
||||
nodes = scopeRef.current.getScopedNodes();
|
||||
nodes = scopeRef.current.getAllNodes();
|
||||
expect(nodes).toEqual([aRef.current, divRef.current, spanRef.current]);
|
||||
});
|
||||
|
||||
it('mixed getParent() and getScopedNodes() works as intended', () => {
|
||||
it('getFirstNode() works as intended', () => {
|
||||
const TestScope = React.unstable_createScope((type, props) => true);
|
||||
const scopeRef = React.createRef();
|
||||
const divRef = React.createRef();
|
||||
const spanRef = React.createRef();
|
||||
const aRef = React.createRef();
|
||||
|
||||
function Test({toggle}) {
|
||||
return toggle ? (
|
||||
<TestScope ref={scopeRef}>
|
||||
<div ref={divRef}>DIV</div>
|
||||
<span ref={spanRef}>SPAN</span>
|
||||
<a ref={aRef}>A</a>
|
||||
</TestScope>
|
||||
) : (
|
||||
<TestScope ref={scopeRef}>
|
||||
<a ref={aRef}>A</a>
|
||||
<div ref={divRef}>DIV</div>
|
||||
<span ref={spanRef}>SPAN</span>
|
||||
</TestScope>
|
||||
);
|
||||
}
|
||||
|
||||
const renderer = ReactTestRenderer.create(<Test toggle={true} />, {
|
||||
createNodeMock: element => {
|
||||
return element;
|
||||
},
|
||||
});
|
||||
let node = scopeRef.current.getFirstNode();
|
||||
expect(node).toEqual(divRef.current);
|
||||
renderer.update(<Test toggle={false} />);
|
||||
node = scopeRef.current.getFirstNode();
|
||||
expect(node).toEqual(aRef.current);
|
||||
});
|
||||
|
||||
it('mixed getParent() and getAllNodes() works as intended', () => {
|
||||
const TestScope = React.unstable_createScope((type, props) => true);
|
||||
const TestScope2 = React.unstable_createScope((type, props) => true);
|
||||
const refA = React.createRef();
|
||||
@@ -326,14 +443,14 @@ describe('ReactScope', () => {
|
||||
});
|
||||
const dParent = refD.current.getParent();
|
||||
expect(dParent).not.toBe(null);
|
||||
expect(dParent.getScopedNodes()).toEqual([
|
||||
expect(dParent.getAllNodes()).toEqual([
|
||||
divA.current,
|
||||
spanB.current,
|
||||
divB.current,
|
||||
]);
|
||||
const cParent = refC.current.getParent();
|
||||
expect(cParent).not.toBe(null);
|
||||
expect(cParent.getScopedNodes()).toEqual([
|
||||
expect(cParent.getAllNodes()).toEqual([
|
||||
spanA.current,
|
||||
divA.current,
|
||||
spanB.current,
|
||||
|
||||
@@ -69,6 +69,7 @@ export const mountFundamentalComponent =
|
||||
$$$hostConfig.mountFundamentalComponent;
|
||||
export const shouldUpdateFundamentalComponent =
|
||||
$$$hostConfig.shouldUpdateFundamentalComponent;
|
||||
export const getInstanceFromNode = $$$hostConfig.getInstanceFromNode;
|
||||
|
||||
// -------------------
|
||||
// Mutation
|
||||
|
||||
@@ -29,6 +29,7 @@ export type Instance = {|
|
||||
props: Object,
|
||||
isHidden: boolean,
|
||||
children: Array<Instance | TextInstance>,
|
||||
internalInstanceHandle: Object,
|
||||
rootContainerInstance: Container,
|
||||
tag: 'INSTANCE',
|
||||
|};
|
||||
@@ -155,6 +156,7 @@ export function createInstance(
|
||||
props: propsToUse,
|
||||
isHidden: false,
|
||||
children: [],
|
||||
internalInstanceHandle,
|
||||
rootContainerInstance,
|
||||
tag: 'INSTANCE',
|
||||
};
|
||||
@@ -351,3 +353,7 @@ export function unmountFundamentalComponent(
|
||||
onUnmount(null, instance, props, state);
|
||||
}
|
||||
}
|
||||
|
||||
export function getInstanceFromNode(node: Object) {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
@@ -170,7 +170,9 @@ export type ReactScopeMethods = {|
|
||||
getChildrenFromRoot(): null | Array<ReactScopeMethods>,
|
||||
getParent(): null | ReactScopeMethods,
|
||||
getProps(): Object,
|
||||
getScopedNodes(): null | Array<Object>,
|
||||
getAllNodes(): null | Array<Object>,
|
||||
getFirstNode(): null | Object,
|
||||
containsNode(Object): boolean,
|
||||
|};
|
||||
|
||||
export type ReactScopeInstance = {|
|
||||
|
||||
Reference in New Issue
Block a user