@samitouri / QOS-React-2 / commits / 1b2159acc3

[React Native] measure calls will now call FabricUIManager (#15324)

* [React Native] Add tests to paper renderer for measure, measureLayout * [React Native] measure calls will now call FabricUIManager The Fabric renderer was previously calling the paper UIManager's measure calls and passing the react tag. This PR changes the renderer to now call FabricUIManager passing the node instead. One of the parts of this that feels more controversial is making NativeMethodsMixin and ReactNative.NativeComponent warn when calling measureLayout in Fabric. As Seb and I decided in https://github.com/facebook/react/pull/15126, it doesn't make sense for a component created with one of these methods to require a native ref but not work the other way around. For example: a.measureLayout(b) might work but b.measureLayout(a) wouldn't. We figure we should keep these consistent and continue migrating things off of NativeMethodsMixin and NativeComponent. If this becomes problematic for the Fabric rollout then we should revisit this. * Fixing Flow * Add FabricUIManager to externals for paper renderer * import * as FabricUIManager from 'FabricUIManager'; * Update tests * Shouldn't have removed UIManager import * Update with the new tests

Eli White committed Apr 9, 2019 at 15:10 UTC 1b2159acc34d9ca2c950e53bfc46db385a75dbad
8 files changed +453 -74
packages/react-native-renderer/src/NativeMethodsMixin.js
+106 -15
@@ -18,6 +18,7 @@ import type {
18 import invariant from 'shared/invariant';
19 // Modules provided by RN:
20 import TextInputState from 'TextInputState';
21 +import * as FabricUIManager from 'FabricUIManager';
22 import UIManager from 'UIManager';
23
24 import {create} from './ReactNativeAttributePayload';
@@ -68,10 +69,33 @@ export default function(
69 * prop](docs/view.html#onlayout) instead.
70 */
71 measure: function(callback: MeasureOnSuccessCallback) {
71 - UIManager.measure(
72 - findNodeHandle(this),
73 - mountSafeCallback_NOT_REALLY_SAFE(this, callback),
74 - );
72 + let maybeInstance;
73 +
74 + // Fiber errors if findNodeHandle is called for an umounted component.
75 + // Tests using ReactTestRenderer will trigger this case indirectly.
76 + // Mimicking stack behavior, we should silently ignore this case.
77 + // TODO Fix ReactTestRenderer so we can remove this try/catch.
78 + try {
79 + maybeInstance = findHostInstance(this);
80 + } catch (error) {}
81 +
82 + // If there is no host component beneath this we should fail silently.
83 + // This is not an error; it could mean a class component rendered null.
84 + if (maybeInstance == null) {
85 + return;
86 + }
87 +
88 + if (maybeInstance.canonical) {
89 + FabricUIManager.measure(
90 + maybeInstance.node,
91 + mountSafeCallback_NOT_REALLY_SAFE(this, callback),
92 + );
93 + } else {
94 + UIManager.measure(
95 + findNodeHandle(this),
96 + mountSafeCallback_NOT_REALLY_SAFE(this, callback),
97 + );
98 + }
99 },
100
101 /**
@@ -90,10 +114,33 @@ export default function(
114 * has been completed in native.
115 */
116 measureInWindow: function(callback: MeasureInWindowOnSuccessCallback) {
93 - UIManager.measureInWindow(
94 - findNodeHandle(this),
95 - mountSafeCallback_NOT_REALLY_SAFE(this, callback),
96 - );
117 + let maybeInstance;
118 +
119 + // Fiber errors if findNodeHandle is called for an umounted component.
120 + // Tests using ReactTestRenderer will trigger this case indirectly.
121 + // Mimicking stack behavior, we should silently ignore this case.
122 + // TODO Fix ReactTestRenderer so we can remove this try/catch.
123 + try {
124 + maybeInstance = findHostInstance(this);
125 + } catch (error) {}
126 +
127 + // If there is no host component beneath this we should fail silently.
128 + // This is not an error; it could mean a class component rendered null.
129 + if (maybeInstance == null) {
130 + return;
131 + }
132 +
133 + if (maybeInstance.canonical) {
134 + FabricUIManager.measureInWindow(
135 + maybeInstance.node,
136 + mountSafeCallback_NOT_REALLY_SAFE(this, callback),
137 + );
138 + } else {
139 + UIManager.measureInWindow(
140 + findNodeHandle(this),
141 + mountSafeCallback_NOT_REALLY_SAFE(this, callback),
142 + );
143 + }
144 },
145
146 /**
@@ -105,16 +152,60 @@ export default function(
152 * `findNodeHandle(component)`.
153 */
154 measureLayout: function(
108 - relativeToNativeNode: number,
155 + relativeToNativeNode: number | Object,
156 onSuccess: MeasureLayoutOnSuccessCallback,
157 onFail: () => void /* currently unused */,
158 ) {
112 - UIManager.measureLayout(
113 - findNodeHandle(this),
114 - relativeToNativeNode,
115 - mountSafeCallback_NOT_REALLY_SAFE(this, onFail),
116 - mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess),
117 - );
159 + let maybeInstance;
160 +
161 + // Fiber errors if findNodeHandle is called for an umounted component.
162 + // Tests using ReactTestRenderer will trigger this case indirectly.
163 + // Mimicking stack behavior, we should silently ignore this case.
164 + // TODO Fix ReactTestRenderer so we can remove this try/catch.
165 + try {
166 + maybeInstance = findHostInstance(this);
167 + } catch (error) {}
168 +
169 + // If there is no host component beneath this we should fail silently.
170 + // This is not an error; it could mean a class component rendered null.
171 + if (maybeInstance == null) {
172 + return;
173 + }
174 +
175 + if (maybeInstance.canonical) {
176 + warningWithoutStack(
177 + false,
178 + 'Warning: measureLayout on components using NativeMethodsMixin ' +
179 + 'or ReactNative.NativeComponent is not currently supported in Fabric. ' +
180 + 'measureLayout must be called on a native ref. Consider using forwardRef.',
181 + );
182 + return;
183 + } else {
184 + let relativeNode;
185 +
186 + if (typeof relativeToNativeNode === 'number') {
187 + // Already a node handle
188 + relativeNode = relativeToNativeNode;
189 + } else if (relativeToNativeNode._nativeTag) {
190 + relativeNode = relativeToNativeNode._nativeTag;
191 + }
192 +
193 + if (relativeNode == null) {
194 + warningWithoutStack(
195 + false,
196 + 'Warning: ref.measureLayout must be called with a node handle or a ref to a native component.',
197 + );
198 +
199 + return;
200 + }
201 +
202 + UIManager.measureLayout(
203 + findNodeHandle(this),
204 + relativeNode,
205 + mountSafeCallback_NOT_REALLY_SAFE(this, onFail),
206 + mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess),
207 + );
208 + }
209 },
210
211 /**
packages/react-native-renderer/src/ReactFabricHostConfig.js
+23 -24
@@ -39,8 +39,10 @@ import {
39 appendChildToSet as appendChildNodeToSet,
40 completeRoot,
41 registerEventHandler,
42 + measure as fabricMeasure,
43 + measureInWindow as fabricMeasureInWindow,
44 + measureLayout as fabricMeasureLayout,
45 } from 'FabricUIManager';
43 -import UIManager from 'UIManager';
46
47 // Counter for uniquely identifying views.
48 // % 10 === 1 means it is a rootTag.
@@ -85,15 +87,18 @@ class ReactFabricHostComponent {
87 _nativeTag: number;
88 viewConfig: ReactNativeBaseComponentViewConfig<>;
89 currentProps: Props;
90 + _internalInstanceHandle: Object;
91
92 constructor(
93 tag: number,
94 viewConfig: ReactNativeBaseComponentViewConfig<>,
95 props: Props,
96 + internalInstanceHandle: Object,
97 ) {
98 this._nativeTag = tag;
99 this.viewConfig = viewConfig;
100 this.currentProps = props;
101 + this._internalInstanceHandle = internalInstanceHandle;
102 }
103
104 blur() {
@@ -105,15 +110,15 @@ class ReactFabricHostComponent {
110 }
111
112 measure(callback: MeasureOnSuccessCallback) {
108 - UIManager.measure(
109 - this._nativeTag,
113 + fabricMeasure(
114 + this._internalInstanceHandle.stateNode.node,
115 mountSafeCallback_NOT_REALLY_SAFE(this, callback),
116 );
117 }
118
119 measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
115 - UIManager.measureInWindow(
116 - this._nativeTag,
120 + fabricMeasureInWindow(
121 + this._internalInstanceHandle.stateNode.node,
122 mountSafeCallback_NOT_REALLY_SAFE(this, callback),
123 );
124 }
@@ -123,32 +128,21 @@ class ReactFabricHostComponent {
128 onSuccess: MeasureLayoutOnSuccessCallback,
129 onFail: () => void /* currently unused */,
130 ) {
126 - let relativeNode;
127 -
128 - if (typeof relativeToNativeNode === 'number') {
129 - // Already a node handle
130 - relativeNode = relativeToNativeNode;
131 - } else if (relativeToNativeNode._nativeTag) {
132 - relativeNode = relativeToNativeNode._nativeTag;
133 - } else if (
134 - relativeToNativeNode.canonical &&
135 - relativeToNativeNode.canonical._nativeTag
131 + if (
132 + typeof relativeToNativeNode === 'number' ||
133 + !(relativeToNativeNode instanceof ReactFabricHostComponent)
134 ) {
137 - relativeNode = relativeToNativeNode.canonical._nativeTag;
138 - }
139 -
140 - if (relativeNode == null) {
135 warningWithoutStack(
136 false,
143 - 'Warning: ref.measureLayout must be called with a node handle or a ref to a native component.',
137 + 'Warning: ref.measureLayout must be called with a ref to a native component.',
138 );
139
140 return;
141 }
142
149 - UIManager.measureLayout(
150 - this._nativeTag,
151 - relativeNode,
143 + fabricMeasureLayout(
144 + this._internalInstanceHandle.stateNode.node,
145 + relativeToNativeNode._internalInstanceHandle.stateNode.node,
146 mountSafeCallback_NOT_REALLY_SAFE(this, onFail),
147 mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess),
148 );
@@ -212,7 +206,12 @@ export function createInstance(
206 internalInstanceHandle, // internalInstanceHandle
207 );
208
215 - const component = new ReactFabricHostComponent(tag, viewConfig, props);
209 + const component = new ReactFabricHostComponent(
210 + tag,
211 + viewConfig,
212 + props,
213 + internalInstanceHandle,
214 + );
215
216 return {
217 node: node,
packages/react-native-renderer/src/ReactNativeComponent.js
+106 -15
@@ -19,6 +19,7 @@ import type {
19 import React from 'react';
20 // Modules provided by RN:
21 import TextInputState from 'TextInputState';
22 +import * as FabricUIManager from 'FabricUIManager';
23 import UIManager from 'UIManager';
24
25 import {create} from './ReactNativeAttributePayload';
@@ -83,10 +84,33 @@ export default function(
84 * [`onLayout` prop](docs/view.html#onlayout) instead.
85 */
86 measure(callback: MeasureOnSuccessCallback): void {
86 - UIManager.measure(
87 - findNodeHandle(this),
88 - mountSafeCallback_NOT_REALLY_SAFE(this, callback),
89 - );
87 + let maybeInstance;
88 +
89 + // Fiber errors if findNodeHandle is called for an umounted component.
90 + // Tests using ReactTestRenderer will trigger this case indirectly.
91 + // Mimicking stack behavior, we should silently ignore this case.
92 + // TODO Fix ReactTestRenderer so we can remove this try/catch.
93 + try {
94 + maybeInstance = findHostInstance(this);
95 + } catch (error) {}
96 +
97 + // If there is no host component beneath this we should fail silently.
98 + // This is not an error; it could mean a class component rendered null.
99 + if (maybeInstance == null) {
100 + return;
101 + }
102 +
103 + if (maybeInstance.canonical) {
104 + FabricUIManager.measure(
105 + maybeInstance.node,
106 + mountSafeCallback_NOT_REALLY_SAFE(this, callback),
107 + );
108 + } else {
109 + UIManager.measure(
110 + findNodeHandle(this),
111 + mountSafeCallback_NOT_REALLY_SAFE(this, callback),
112 + );
113 + }
114 }
115
116 /**
@@ -103,10 +127,33 @@ export default function(
127 * These values are not available until after natives rendering completes.
128 */
129 measureInWindow(callback: MeasureInWindowOnSuccessCallback): void {
106 - UIManager.measureInWindow(
107 - findNodeHandle(this),
108 - mountSafeCallback_NOT_REALLY_SAFE(this, callback),
109 - );
130 + let maybeInstance;
131 +
132 + // Fiber errors if findNodeHandle is called for an umounted component.
133 + // Tests using ReactTestRenderer will trigger this case indirectly.
134 + // Mimicking stack behavior, we should silently ignore this case.
135 + // TODO Fix ReactTestRenderer so we can remove this try/catch.
136 + try {
137 + maybeInstance = findHostInstance(this);
138 + } catch (error) {}
139 +
140 + // If there is no host component beneath this we should fail silently.
141 + // This is not an error; it could mean a class component rendered null.
142 + if (maybeInstance == null) {
143 + return;
144 + }
145 +
146 + if (maybeInstance.canonical) {
147 + FabricUIManager.measureInWindow(
148 + maybeInstance.node,
149 + mountSafeCallback_NOT_REALLY_SAFE(this, callback),
150 + );
151 + } else {
152 + UIManager.measureInWindow(
153 + findNodeHandle(this),
154 + mountSafeCallback_NOT_REALLY_SAFE(this, callback),
155 + );
156 + }
157 }
158
159 /**
@@ -116,16 +163,60 @@ export default function(
163 * Obtain a native node handle with `ReactNative.findNodeHandle(component)`.
164 */
165 measureLayout(
119 - relativeToNativeNode: number,
166 + relativeToNativeNode: number | Object,
167 onSuccess: MeasureLayoutOnSuccessCallback,
168 onFail: () => void /* currently unused */,
169 ): void {
123 - UIManager.measureLayout(
124 - findNodeHandle(this),
125 - relativeToNativeNode,
126 - mountSafeCallback_NOT_REALLY_SAFE(this, onFail),
127 - mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess),
128 - );
170 + let maybeInstance;
171 +
172 + // Fiber errors if findNodeHandle is called for an umounted component.
173 + // Tests using ReactTestRenderer will trigger this case indirectly.
174 + // Mimicking stack behavior, we should silently ignore this case.
175 + // TODO Fix ReactTestRenderer so we can remove this try/catch.
176 + try {
177 + maybeInstance = findHostInstance(this);
178 + } catch (error) {}
179 +
180 + // If there is no host component beneath this we should fail silently.
181 + // This is not an error; it could mean a class component rendered null.
182 + if (maybeInstance == null) {
183 + return;
184 + }
185 +
186 + if (maybeInstance.canonical) {
187 + warningWithoutStack(
188 + false,
189 + 'Warning: measureLayout on components using NativeMethodsMixin ' +
190 + 'or ReactNative.NativeComponent is not currently supported in Fabric. ' +
191 + 'measureLayout must be called on a native ref. Consider using forwardRef.',
192 + );
193 + return;
194 + } else {
195 + let relativeNode;
196 +
197 + if (typeof relativeToNativeNode === 'number') {
198 + // Already a node handle
199 + relativeNode = relativeToNativeNode;
200 + } else if (relativeToNativeNode._nativeTag) {
201 + relativeNode = relativeToNativeNode._nativeTag;
202 + }
203 +
204 + if (relativeNode == null) {
205 + warningWithoutStack(
206 + false,
207 + 'Warning: ref.measureLayout must be called with a node handle or a ref to a native component.',
208 + );
209 +
210 + return;
211 + }
212 +
213 + UIManager.measureLayout(
214 + findNodeHandle(this),
215 + relativeNode,
216 + mountSafeCallback_NOT_REALLY_SAFE(this, onFail),
217 + mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess),
218 + );
219 + }
220 }
221
222 /**
packages/react-native-renderer/src/ReactNativeTypes.js
+2 -2
@@ -89,7 +89,7 @@ class ReactNativeComponent<Props> extends React.Component<Props> {
89 measure(callback: MeasureOnSuccessCallback): void {}
90 measureInWindow(callback: MeasureInWindowOnSuccessCallback): void {}
91 measureLayout(
92 - relativeToNativeNode: number,
92 + relativeToNativeNode: number | Object,
93 onSuccess: MeasureLayoutOnSuccessCallback,
94 onFail?: () => void,
95 ): void {}
@@ -106,7 +106,7 @@ export type NativeMethodsMixinType = {
106 measure(callback: MeasureOnSuccessCallback): void,
107 measureInWindow(callback: MeasureInWindowOnSuccessCallback): void,
108 measureLayout(
109 - relativeToNativeNode: number,
109 + relativeToNativeNode: number | Object,
110 onSuccess: MeasureLayoutOnSuccessCallback,
111 onFail: () => void,
112 ): void,
packages/react-native-renderer/src/__mocks__/FabricUIManager.js
+51
@@ -119,6 +119,57 @@ const RCTFabricUIManager = {
119 }),
120
121 registerEventHandler: jest.fn(function registerEventHandler(callback) {}),
122 +
123 + measure: jest.fn(function measure(node, callback) {
124 + invariant(
125 + typeof node === 'object',
126 + 'Expected node to be an object, was passed "%s"',
127 + typeof node,
128 + );
129 + invariant(
130 + typeof node.viewName === 'string',
131 + 'Expected node to be a host node.',
132 + );
133 + callback(10, 10, 100, 100, 0, 0);
134 + }),
135 + measureInWindow: jest.fn(function measureInWindow(node, callback) {
136 + invariant(
137 + typeof node === 'object',
138 + 'Expected node to be an object, was passed "%s"',
139 + typeof node,
140 + );
141 + invariant(
142 + typeof node.viewName === 'string',
143 + 'Expected node to be a host node.',
144 + );
145 + callback(10, 10, 100, 100);
146 + }),
147 + measureLayout: jest.fn(function measureLayout(
148 + node,
149 + relativeNode,
150 + fail,
151 + success,
152 + ) {
153 + invariant(
154 + typeof node === 'object',
155 + 'Expected node to be an object, was passed "%s"',
156 + typeof node,
157 + );
158 + invariant(
159 + typeof node.viewName === 'string',
160 + 'Expected node to be a host node.',
161 + );
162 + invariant(
163 + typeof relativeNode === 'object',
164 + 'Expected relative node to be an object, was passed "%s"',
165 + typeof relativeNode,
166 + );
167 + invariant(
168 + typeof relativeNode.viewName === 'string',
169 + 'Expected relative node to be a host node.',
170 + );
171 + success(1, 1, 100, 100);
172 + }),
173 };
174
175 module.exports = RCTFabricUIManager;
packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js
+145 -18
@@ -302,6 +302,88 @@ describe('ReactFabric', () => {
302 });
303 });
304
305 + it('should call FabricUIManager.measure on ref.measure', () => {
306 + const View = createReactNativeComponentClass('RCTView', () => ({
307 + validAttributes: {foo: true},
308 + uiViewClassName: 'RCTView',
309 + }));
310 +
311 + class Subclass extends ReactFabric.NativeComponent {
312 + render() {
313 + return <View>{this.props.children}</View>;
314 + }
315 + }
316 +
317 + const CreateClass = createReactClass({
318 + mixins: [NativeMethodsMixin],
319 + render() {
320 + return <View>{this.props.children}</View>;
321 + },
322 + });
323 +
324 + [View, Subclass, CreateClass].forEach(Component => {
325 + FabricUIManager.measure.mockClear();
326 +
327 + let viewRef;
328 + ReactFabric.render(
329 + <Component
330 + ref={ref => {
331 + viewRef = ref;
332 + }}
333 + />,
334 + 11,
335 + );
336 +
337 + expect(FabricUIManager.measure).not.toBeCalled();
338 + const successCallback = jest.fn();
339 + viewRef.measure(successCallback);
340 + expect(FabricUIManager.measure).toHaveBeenCalledTimes(1);
341 + expect(successCallback).toHaveBeenCalledTimes(1);
342 + expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100, 0, 0);
343 + });
344 + });
345 +
346 + it('should call FabricUIManager.measureInWindow on ref.measureInWindow', () => {
347 + const View = createReactNativeComponentClass('RCTView', () => ({
348 + validAttributes: {foo: true},
349 + uiViewClassName: 'RCTView',
350 + }));
351 +
352 + class Subclass extends ReactFabric.NativeComponent {
353 + render() {
354 + return <View>{this.props.children}</View>;
355 + }
356 + }
357 +
358 + const CreateClass = createReactClass({
359 + mixins: [NativeMethodsMixin],
360 + render() {
361 + return <View>{this.props.children}</View>;
362 + },
363 + });
364 +
365 + [View, Subclass, CreateClass].forEach(Component => {
366 + FabricUIManager.measureInWindow.mockClear();
367 +
368 + let viewRef;
369 + ReactFabric.render(
370 + <Component
371 + ref={ref => {
372 + viewRef = ref;
373 + }}
374 + />,
375 + 11,
376 + );
377 +
378 + expect(FabricUIManager.measureInWindow).not.toBeCalled();
379 + const successCallback = jest.fn();
380 + viewRef.measureInWindow(successCallback);
381 + expect(FabricUIManager.measureInWindow).toHaveBeenCalledTimes(1);
382 + expect(successCallback).toHaveBeenCalledTimes(1);
383 + expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100);
384 + });
385 + });
386 +
387 it('should support ref in ref.measureLayout', () => {
388 const View = createReactNativeComponentClass('RCTView', () => ({
389 validAttributes: {foo: true},
@@ -309,7 +391,7 @@ describe('ReactFabric', () => {
391 }));
392
393 [View].forEach(Component => {
312 - UIManager.measureLayout.mockReset();
394 + FabricUIManager.measureLayout.mockClear();
395
396 let viewRef;
397 let otherRef;
@@ -330,30 +412,75 @@ describe('ReactFabric', () => {
412 11,
413 );
414
333 - expect(UIManager.measureLayout).not.toBeCalled();
334 -
415 + expect(FabricUIManager.measureLayout).not.toBeCalled();
416 const successCallback = jest.fn();
417 const failureCallback = jest.fn();
418 viewRef.measureLayout(otherRef, successCallback, failureCallback);
419 + expect(FabricUIManager.measureLayout).toHaveBeenCalledTimes(1);
420 + expect(successCallback).toHaveBeenCalledTimes(1);
421 + expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
422 + });
423 + });
424 +
425 + it('should warn when calling measureLayout on Subclass and NativeMethodsMixin', () => {
426 + const View = createReactNativeComponentClass('RCTView', () => ({
427 + validAttributes: {foo: true},
428 + uiViewClassName: 'RCTView',
429 + }));
430 +
431 + class Subclass extends ReactFabric.NativeComponent {
432 + render() {
433 + return <View>{this.props.children}</View>;
434 + }
435 + }
436 +
437 + const CreateClass = createReactClass({
438 + mixins: [NativeMethodsMixin],
439 + render() {
440 + return <View>{this.props.children}</View>;
441 + },
442 + });
443
339 - expect(UIManager.measureLayout).toHaveBeenCalledTimes(1);
340 - expect(UIManager.measureLayout).toHaveBeenCalledWith(
341 - expect.any(Number),
342 - expect.any(Number),
343 - expect.any(Function),
344 - expect.any(Function),
444 + [Subclass, CreateClass].forEach(Component => {
445 + FabricUIManager.measureLayout.mockReset();
446 +
447 + let viewRef;
448 + let otherRef;
449 + ReactFabric.render(
450 + <Component>
451 + <Component
452 + foo="bar"
453 + ref={ref => {
454 + viewRef = ref;
455 + }}
456 + />
457 + <View
458 + ref={ref => {
459 + otherRef = ref;
460 + }}
461 + />
462 + </Component>,
463 + 11,
464 );
465
347 - const args = UIManager.measureLayout.mock.calls[0];
348 - expect(args[0]).not.toEqual(args[1]);
349 - expect(successCallback).not.toBeCalled();
350 - expect(failureCallback).not.toBeCalled();
351 - args[2]('fail');
352 - expect(failureCallback).toBeCalledWith('fail');
466 + const successCallback = jest.fn();
467 + const failureCallback = jest.fn();
468 +
469 + expect(() => {
470 + viewRef.measureLayout(otherRef, successCallback, failureCallback);
471 + }).toWarnDev(
472 + [
473 + 'Warning: measureLayout on components using NativeMethodsMixin ' +
474 + 'or ReactNative.NativeComponent is not currently supported in Fabric. ' +
475 + 'measureLayout must be called on a native ref. Consider using forwardRef.',
476 + ],
477 + {
478 + withoutStack: true,
479 + },
480 + );
481
354 - expect(successCallback).not.toBeCalled();
355 - args[3]('success');
356 - expect(successCallback).toBeCalledWith('success');
482 + expect(FabricUIManager.measureLayout).not.toBeCalled();
483 + expect(UIManager.measureLayout).not.toBeCalled();
484 });
485 });
486
scripts/flow/react-native-host-hooks.js
+18
@@ -10,6 +10,9 @@
10 /* eslint-disable */
11
12 import type {
13 + MeasureOnSuccessCallback,
14 + MeasureInWindowOnSuccessCallback,
15 + MeasureLayoutOnSuccessCallback,
16 ReactNativeBaseComponentViewConfig,
17 ViewConfigGetter,
18 } from 'react-native-renderer/src/ReactNativeTypes';
@@ -124,6 +127,21 @@ declare module 'FabricUIManager' {
127 payload: Object,
128 ) => void,
129 ): void;
130 +
131 + declare function measure(
132 + node: Node,
133 + callback: MeasureOnSuccessCallback,
134 + ): void;
135 + declare function measureInWindow(
136 + node: Node,
137 + callback: MeasureInWindowOnSuccessCallback,
138 + ): void;
139 + declare function measureLayout(
140 + node: Node,
141 + relativeNode: Node,
142 + onFail: () => void,
143 + onSuccess: MeasureLayoutOnSuccessCallback,
144 + ): void;
145 }
146
147 declare module 'View' {
scripts/rollup/bundles.js
+2
@@ -224,6 +224,7 @@ const bundles = [
224 'RCTEventEmitter',
225 'TextInputState',
226 'UIManager',
227 + 'FabricUIManager',
228 'deepDiffer',
229 'deepFreezeAndThrowOnMutationInDev',
230 'flattenStyle',
@@ -243,6 +244,7 @@ const bundles = [
244 'RCTEventEmitter',
245 'TextInputState',
246 'UIManager',
247 + 'FabricUIManager',
248 'deepDiffer',
249 'deepFreezeAndThrowOnMutationInDev',
250 'flattenStyle',