Add findHostInstance_deprecated to the React Native Renderer (#17224)
Eli White committed
Oct 30, 2019 at 11:42 UTC
515746c217ef97a1a5745f15bf9f1cae3c84d2c6
5 files changed
+294
-2
packages/react-native-renderer/src/ReactFabric.js
+52
-1
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import type {ReactFabricType} from './ReactNativeTypes';
10
+import type {ReactFabricType, HostComponent} from './ReactNativeTypes';
11
import type {ReactNodeList} from 'shared/ReactTypes';
12
13
import './ReactFabricInjection';
@@ -44,6 +44,54 @@ import warningWithoutStack from 'shared/warningWithoutStack';
44
45
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
46
47
+function findHostInstance_deprecated(
48
+ componentOrHandle: any,
49
+): ?HostComponent<mixed> {
50
+ if (__DEV__) {
51
+ const owner = ReactCurrentOwner.current;
52
+ if (owner !== null && owner.stateNode !== null) {
53
+ warningWithoutStack(
54
+ owner.stateNode._warnedAboutRefsInRender,
55
+ '%s is accessing findNodeHandle inside its render(). ' +
56
+ 'render() should be a pure function of props and state. It should ' +
57
+ 'never access something that requires stale data from the previous ' +
58
+ 'render, such as refs. Move this logic to componentDidMount and ' +
59
+ 'componentDidUpdate instead.',
60
+ getComponentName(owner.type) || 'A component',
61
+ );
62
+
63
+ owner.stateNode._warnedAboutRefsInRender = true;
64
+ }
65
+ }
66
+ if (componentOrHandle == null) {
67
+ return null;
68
+ }
69
+ if (componentOrHandle._nativeTag) {
70
+ return componentOrHandle;
71
+ }
72
+ if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) {
73
+ return componentOrHandle.canonical;
74
+ }
75
+ let hostInstance;
76
+ if (__DEV__) {
77
+ hostInstance = findHostInstanceWithWarning(
78
+ componentOrHandle,
79
+ 'findHostInstance_deprecated',
80
+ );
81
+ } else {
82
+ hostInstance = findHostInstance(componentOrHandle);
83
+ }
84
+
85
+ if (hostInstance == null) {
86
+ return hostInstance;
87
+ }
88
+ if ((hostInstance: any).canonical) {
89
+ // Fabric
90
+ return (hostInstance: any).canonical;
91
+ }
92
+ return hostInstance;
93
+}
94
+
95
function findNodeHandle(componentOrHandle: any): ?number {
96
if (__DEV__) {
97
const owner = ReactCurrentOwner.current;
@@ -108,6 +156,9 @@ const roots = new Map();
156
const ReactFabric: ReactFabricType = {
157
NativeComponent: ReactNativeComponent(findNodeHandle, findHostInstance),
158
159
+ // This is needed for implementation details of TouchableNativeFeedback
160
+ // Remove this once TouchableNativeFeedback doesn't use cloneElement
161
+ findHostInstance_deprecated,
162
findNodeHandle,
163
164
dispatchCommand(handle: any, command: string, args: Array<any>) {
packages/react-native-renderer/src/ReactNativeRenderer.js
+52
-1
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import type {ReactNativeType} from './ReactNativeTypes';
10
+import type {ReactNativeType, HostComponent} from './ReactNativeTypes';
11
import type {ReactNodeList} from 'shared/ReactTypes';
12
13
import './ReactNativeInjection';
@@ -47,6 +47,54 @@ import warningWithoutStack from 'shared/warningWithoutStack';
47
48
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
49
50
+function findHostInstance_deprecated(
51
+ componentOrHandle: any,
52
+): ?HostComponent<mixed> {
53
+ if (__DEV__) {
54
+ const owner = ReactCurrentOwner.current;
55
+ if (owner !== null && owner.stateNode !== null) {
56
+ warningWithoutStack(
57
+ owner.stateNode._warnedAboutRefsInRender,
58
+ '%s is accessing findNodeHandle inside its render(). ' +
59
+ 'render() should be a pure function of props and state. It should ' +
60
+ 'never access something that requires stale data from the previous ' +
61
+ 'render, such as refs. Move this logic to componentDidMount and ' +
62
+ 'componentDidUpdate instead.',
63
+ getComponentName(owner.type) || 'A component',
64
+ );
65
+
66
+ owner.stateNode._warnedAboutRefsInRender = true;
67
+ }
68
+ }
69
+ if (componentOrHandle == null) {
70
+ return null;
71
+ }
72
+ if (componentOrHandle._nativeTag) {
73
+ return componentOrHandle;
74
+ }
75
+ if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) {
76
+ return componentOrHandle.canonical;
77
+ }
78
+ let hostInstance;
79
+ if (__DEV__) {
80
+ hostInstance = findHostInstanceWithWarning(
81
+ componentOrHandle,
82
+ 'findHostInstance_deprecated',
83
+ );
84
+ } else {
85
+ hostInstance = findHostInstance(componentOrHandle);
86
+ }
87
+
88
+ if (hostInstance == null) {
89
+ return hostInstance;
90
+ }
91
+ if ((hostInstance: any).canonical) {
92
+ // Fabric
93
+ return (hostInstance: any).canonical;
94
+ }
95
+ return hostInstance;
96
+}
97
+
98
function findNodeHandle(componentOrHandle: any): ?number {
99
if (__DEV__) {
100
const owner = ReactCurrentOwner.current;
@@ -117,6 +165,9 @@ const roots = new Map();
165
const ReactNativeRenderer: ReactNativeType = {
166
NativeComponent: ReactNativeComponent(findNodeHandle, findHostInstance),
167
168
+ // This is needed for implementation details of TouchableNativeFeedback
169
+ // Remove this once TouchableNativeFeedback doesn't use cloneElement
170
+ findHostInstance_deprecated,
171
findNodeHandle,
172
173
dispatchCommand(handle: any, command: string, args: Array<any>) {
packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js
+75
@@ -779,6 +779,81 @@ describe('ReactFabric', () => {
779
expect(touchStart2).toBeCalled();
780
});
781
782
+ it('findHostInstance_deprecated should warn if used to find a host component inside StrictMode', () => {
783
+ const View = createReactNativeComponentClass('RCTView', () => ({
784
+ validAttributes: {foo: true},
785
+ uiViewClassName: 'RCTView',
786
+ }));
787
+
788
+ let parent = undefined;
789
+ let child = undefined;
790
+
791
+ class ContainsStrictModeChild extends React.Component {
792
+ render() {
793
+ return (
794
+ <StrictMode>
795
+ <View ref={n => (child = n)} />
796
+ </StrictMode>
797
+ );
798
+ }
799
+ }
800
+
801
+ ReactFabric.render(<ContainsStrictModeChild ref={n => (parent = n)} />, 11);
802
+
803
+ let match;
804
+ expect(
805
+ () => (match = ReactFabric.findHostInstance_deprecated(parent)),
806
+ ).toWarnDev([
807
+ 'Warning: findHostInstance_deprecated is deprecated in StrictMode. ' +
808
+ 'findHostInstance_deprecated was passed an instance of ContainsStrictModeChild which renders StrictMode children. ' +
809
+ 'Instead, add a ref directly to the element you want to reference. ' +
810
+ 'Learn more about using refs safely here: ' +
811
+ 'https://fb.me/react-strict-mode-find-node' +
812
+ '\n in RCTView (at **)' +
813
+ '\n in StrictMode (at **)' +
814
+ '\n in ContainsStrictModeChild (at **)',
815
+ ]);
816
+ expect(match).toBe(child);
817
+ });
818
+
819
+ it('findHostInstance_deprecated should warn if passed a component that is inside StrictMode', () => {
820
+ const View = createReactNativeComponentClass('RCTView', () => ({
821
+ validAttributes: {foo: true},
822
+ uiViewClassName: 'RCTView',
823
+ }));
824
+
825
+ let parent = undefined;
826
+ let child = undefined;
827
+
828
+ class IsInStrictMode extends React.Component {
829
+ render() {
830
+ return <View ref={n => (child = n)} />;
831
+ }
832
+ }
833
+
834
+ ReactFabric.render(
835
+ <StrictMode>
836
+ <IsInStrictMode ref={n => (parent = n)} />
837
+ </StrictMode>,
838
+ 11,
839
+ );
840
+
841
+ let match;
842
+ expect(
843
+ () => (match = ReactFabric.findHostInstance_deprecated(parent)),
844
+ ).toWarnDev([
845
+ 'Warning: findHostInstance_deprecated is deprecated in StrictMode. ' +
846
+ 'findHostInstance_deprecated was passed an instance of IsInStrictMode which is inside StrictMode. ' +
847
+ 'Instead, add a ref directly to the element you want to reference. ' +
848
+ 'Learn more about using refs safely here: ' +
849
+ 'https://fb.me/react-strict-mode-find-node' +
850
+ '\n in RCTView (at **)' +
851
+ '\n in IsInStrictMode (at **)' +
852
+ '\n in StrictMode (at **)',
853
+ ]);
854
+ expect(match).toBe(child);
855
+ });
856
+
857
it('findNodeHandle should warn if used to find a host component inside StrictMode', () => {
858
const View = createReactNativeComponentClass('RCTView', () => ({
859
validAttributes: {foo: true},
packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js
+40
@@ -34,6 +34,26 @@ describe('created with ReactFabric called with ReactNative', () => {
34
.ReactNativeViewConfigRegistry.register;
35
});
36
37
+ it('find Fabric instances with the RN renderer', () => {
38
+ const View = createReactNativeComponentClass('RCTView', () => ({
39
+ validAttributes: {title: true},
40
+ uiViewClassName: 'RCTView',
41
+ }));
42
+
43
+ let ref = React.createRef();
44
+
45
+ class Component extends React.Component {
46
+ render() {
47
+ return <View title="foo" />;
48
+ }
49
+ }
50
+
51
+ ReactFabric.render(<Component ref={ref} />, 11);
52
+
53
+ let instance = ReactNative.findHostInstance_deprecated(ref.current);
54
+ expect(instance._nativeTag).toBe(2);
55
+ });
56
+
57
it('find Fabric nodes with the RN renderer', () => {
58
const View = createReactNativeComponentClass('RCTView', () => ({
59
validAttributes: {title: true},
@@ -94,6 +114,26 @@ describe('created with ReactNative called with ReactFabric', () => {
114
.ReactNativeViewConfigRegistry.register;
115
});
116
117
+ it('find Paper instances with the Fabric renderer', () => {
118
+ const View = createReactNativeComponentClass('RCTView', () => ({
119
+ validAttributes: {title: true},
120
+ uiViewClassName: 'RCTView',
121
+ }));
122
+
123
+ let ref = React.createRef();
124
+
125
+ class Component extends React.Component {
126
+ render() {
127
+ return <View title="foo" />;
128
+ }
129
+ }
130
+
131
+ ReactNative.render(<Component ref={ref} />, 11);
132
+
133
+ let instance = ReactFabric.findHostInstance_deprecated(ref.current);
134
+ expect(instance._nativeTag).toBe(3);
135
+ });
136
+
137
it('find Paper nodes with the Fabric renderer', () => {
138
const View = createReactNativeComponentClass('RCTView', () => ({
139
validAttributes: {title: true},
packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js
+75
@@ -541,6 +541,81 @@ describe('ReactNative', () => {
541
);
542
});
543
544
+ it('findHostInstance_deprecated should warn if used to find a host component inside StrictMode', () => {
545
+ const View = createReactNativeComponentClass('RCTView', () => ({
546
+ validAttributes: {foo: true},
547
+ uiViewClassName: 'RCTView',
548
+ }));
549
+
550
+ let parent = undefined;
551
+ let child = undefined;
552
+
553
+ class ContainsStrictModeChild extends React.Component {
554
+ render() {
555
+ return (
556
+ <StrictMode>
557
+ <View ref={n => (child = n)} />
558
+ </StrictMode>
559
+ );
560
+ }
561
+ }
562
+
563
+ ReactNative.render(<ContainsStrictModeChild ref={n => (parent = n)} />, 11);
564
+
565
+ let match;
566
+ expect(
567
+ () => (match = ReactNative.findHostInstance_deprecated(parent)),
568
+ ).toWarnDev([
569
+ 'Warning: findHostInstance_deprecated is deprecated in StrictMode. ' +
570
+ 'findHostInstance_deprecated was passed an instance of ContainsStrictModeChild which renders StrictMode children. ' +
571
+ 'Instead, add a ref directly to the element you want to reference. ' +
572
+ 'Learn more about using refs safely here: ' +
573
+ 'https://fb.me/react-strict-mode-find-node' +
574
+ '\n in RCTView (at **)' +
575
+ '\n in StrictMode (at **)' +
576
+ '\n in ContainsStrictModeChild (at **)',
577
+ ]);
578
+ expect(match).toBe(child);
579
+ });
580
+
581
+ it('findHostInstance_deprecated should warn if passed a component that is inside StrictMode', () => {
582
+ const View = createReactNativeComponentClass('RCTView', () => ({
583
+ validAttributes: {foo: true},
584
+ uiViewClassName: 'RCTView',
585
+ }));
586
+
587
+ let parent = undefined;
588
+ let child = undefined;
589
+
590
+ class IsInStrictMode extends React.Component {
591
+ render() {
592
+ return <View ref={n => (child = n)} />;
593
+ }
594
+ }
595
+
596
+ ReactNative.render(
597
+ <StrictMode>
598
+ <IsInStrictMode ref={n => (parent = n)} />
599
+ </StrictMode>,
600
+ 11,
601
+ );
602
+
603
+ let match;
604
+ expect(
605
+ () => (match = ReactNative.findHostInstance_deprecated(parent)),
606
+ ).toWarnDev([
607
+ 'Warning: findHostInstance_deprecated is deprecated in StrictMode. ' +
608
+ 'findHostInstance_deprecated was passed an instance of IsInStrictMode which is inside StrictMode. ' +
609
+ 'Instead, add a ref directly to the element you want to reference. ' +
610
+ 'Learn more about using refs safely here: ' +
611
+ 'https://fb.me/react-strict-mode-find-node' +
612
+ '\n in RCTView (at **)' +
613
+ '\n in IsInStrictMode (at **)' +
614
+ '\n in StrictMode (at **)',
615
+ ]);
616
+ expect(match).toBe(child);
617
+ });
618
+
619
it('findNodeHandle should warn if used to find a host component inside StrictMode', () => {
620
const View = createReactNativeComponentClass('RCTView', () => ({
621
validAttributes: {foo: true},