@samitouri / QOS-React-2 / commits / 9ff60ff16b

[react-events] Fix Scope listener issue (#16658)

Dominic Gannaway committed Sep 4, 2019 at 18:57 UTC 9ff60ff16b289a9efce46e21fe223aec1638b5d6
4 files changed +87 -25
packages/react-reconciler/src/ReactFiberCommitWork.js
+2 -2
@@ -1343,7 +1343,7 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
1343 const prevListeners = oldProps.listeners;
1344 const nextListeners = newProps.listeners;
1345 if (prevListeners !== nextListeners) {
1346 - updateEventListeners(nextListeners, finishedWork);
1346 + updateEventListeners(nextListeners, finishedWork, null);
1347 }
1348 }
1349 }
@@ -1400,7 +1400,7 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
1400 const prevListeners = oldProps.listeners;
1401 const nextListeners = newProps.listeners;
1402 if (prevListeners !== nextListeners) {
1403 - updateEventListeners(nextListeners, finishedWork);
1403 + updateEventListeners(nextListeners, finishedWork, null);
1404 }
1405 }
1406 }
packages/react-reconciler/src/ReactFiberCompleteWork.js
+16 -3
@@ -724,7 +724,11 @@ function completeWork(
724 if (enableFlareAPI) {
725 const listeners = newProps.listeners;
726 if (listeners != null) {
727 - updateEventListeners(listeners, workInProgress);
727 + updateEventListeners(
728 + listeners,
729 + workInProgress,
730 + rootContainerInstance,
731 + );
732 }
733 }
734 } else {
@@ -744,7 +748,11 @@ function completeWork(
748 if (enableFlareAPI) {
749 const listeners = newProps.listeners;
750 if (listeners != null) {
747 - updateEventListeners(listeners, workInProgress);
751 + updateEventListeners(
752 + listeners,
753 + workInProgress,
754 + rootContainerInstance,
755 + );
756 }
757 }
758
@@ -1233,7 +1241,12 @@ function completeWork(
1241 if (enableFlareAPI) {
1242 const listeners = newProps.listeners;
1243 if (listeners != null) {
1236 - updateEventListeners(listeners, workInProgress);
1244 + const rootContainerInstance = getRootHostContainer();
1245 + updateEventListeners(
1246 + listeners,
1247 + workInProgress,
1248 + rootContainerInstance,
1249 + );
1250 }
1251 }
1252 if (workInProgress.ref !== null) {
packages/react-reconciler/src/ReactFiberEvents.js
+44 -17
@@ -8,7 +8,7 @@
8 */
9
10 import type {Fiber} from './ReactFiber';
11 -import type {Instance} from './ReactFiberHostConfig';
11 +import type {Container, Instance} from './ReactFiberHostConfig';
12 import type {
13 ReactEventResponder,
14 ReactEventResponderInstance,
@@ -53,6 +53,7 @@ function mountEventResponder(
53 ReactEventResponder<any, any>,
54 ReactEventResponderInstance<any, any>,
55 >,
56 + rootContainerInstance: null | Container,
57 ) {
58 let responderState = emptyObject;
59 const getInitialState = responder.getInitialState;
@@ -65,25 +66,28 @@ function mountEventResponder(
66 responderState,
67 fiber,
68 );
68 - let instance = null;
69 - let node = fiber;
70 - while (node !== null) {
71 - const tag = node.tag;
72 - if (tag === HostComponent) {
73 - instance = node.stateNode;
74 - break;
75 - } else if (tag === HostRoot) {
76 - instance = node.stateNode.containerInfo;
77 - break;
69 +
70 + if (!rootContainerInstance) {
71 + let node = fiber;
72 + while (node !== null) {
73 + const tag = node.tag;
74 + if (tag === HostComponent) {
75 + rootContainerInstance = node.stateNode;
76 + break;
77 + } else if (tag === HostRoot) {
78 + rootContainerInstance = node.stateNode.containerInfo;
79 + break;
80 + }
81 + node = node.return;
82 }
79 - node = node.return;
83 }
84 +
85 mountResponderInstance(
86 responder,
87 responderInstance,
88 responderProps,
89 responderState,
86 - ((instance: any): Instance),
90 + ((rootContainerInstance: any): Instance),
91 );
92 respondersMap.set(responder, responderInstance);
93 }
@@ -96,6 +100,7 @@ function updateEventListener(
100 ReactEventResponder<any, any>,
101 ReactEventResponderInstance<any, any>,
102 >,
103 + rootContainerInstance: null | Container,
104 ): void {
105 let responder;
106 let props;
@@ -127,7 +132,13 @@ function updateEventListener(
132
133 if (responderInstance === undefined) {
134 // Mount (happens in either complete or commit phase)
130 - mountEventResponder(responder, listenerProps, fiber, respondersMap);
135 + mountEventResponder(
136 + responder,
137 + listenerProps,
138 + fiber,
139 + respondersMap,
140 + rootContainerInstance,
141 + );
142 } else {
143 // Update (happens during commit phase only)
144 responderInstance.props = listenerProps;
@@ -135,7 +146,11 @@ function updateEventListener(
146 }
147 }
148
138 -export function updateEventListeners(listeners: any, fiber: Fiber): void {
149 +export function updateEventListeners(
150 + listeners: any,
151 + fiber: Fiber,
152 + rootContainerInstance: null | Container,
153 +): void {
154 const visistedResponders = new Set();
155 let dependencies = fiber.dependencies;
156 if (listeners != null) {
@@ -153,10 +168,22 @@ export function updateEventListeners(listeners: any, fiber: Fiber): void {
168 if (isArray(listeners)) {
169 for (let i = 0, length = listeners.length; i < length; i++) {
170 const listener = listeners[i];
156 - updateEventListener(listener, fiber, visistedResponders, respondersMap);
171 + updateEventListener(
172 + listener,
173 + fiber,
174 + visistedResponders,
175 + respondersMap,
176 + rootContainerInstance,
177 + );
178 }
179 } else {
159 - updateEventListener(listeners, fiber, visistedResponders, respondersMap);
180 + updateEventListener(
181 + listeners,
182 + fiber,
183 + visistedResponders,
184 + respondersMap,
185 + rootContainerInstance,
186 + );
187 }
188 }
189 if (dependencies !== null) {
packages/react-reconciler/src/__tests__/ReactScope-test.internal.js
+25 -3
@@ -199,11 +199,11 @@ describe('ReactScope', () => {
199 });
200
201 it('event responders can be attached to scopes', () => {
202 + let onKeyDown = jest.fn();
203 const TestScope = React.unstable_createScope((type, props) => true);
203 - const onKeyDown = jest.fn();
204 const ref = React.createRef();
205 const useKeyboard = require('react-events/keyboard').useKeyboard;
206 - const Component = () => {
206 + let Component = () => {
207 const listener = useKeyboard({
208 onKeyDown,
209 });
@@ -215,7 +215,29 @@ describe('ReactScope', () => {
215 };
216 ReactDOM.render(<Component />, container);
217
218 - const target = createEventTarget(ref.current);
218 + let target = createEventTarget(ref.current);
219 + target.keydown({key: 'Q'});
220 + expect(onKeyDown).toHaveBeenCalledTimes(1);
221 + expect(onKeyDown).toHaveBeenCalledWith(
222 + expect.objectContaining({key: 'Q', type: 'keydown'}),
223 + );
224 +
225 + onKeyDown = jest.fn();
226 + Component = () => {
227 + const listener = useKeyboard({
228 + onKeyDown,
229 + });
230 + return (
231 + <div>
232 + <TestScope listeners={listener}>
233 + <div ref={ref} />
234 + </TestScope>
235 + </div>
236 + );
237 + };
238 + ReactDOM.render(<Component />, container);
239 +
240 + target = createEventTarget(ref.current);
241 target.keydown({key: 'Q'});
242 expect(onKeyDown).toHaveBeenCalledTimes(1);
243 expect(onKeyDown).toHaveBeenCalledWith(