Skip to content

Commit 515746c

Browse files
authored
Add findHostInstance_deprecated to the React Native Renderer (#17224)
1 parent 9a35adc commit 515746c

File tree

5 files changed

+294
-2
lines changed

5 files changed

+294
-2
lines changed

packages/react-native-renderer/src/ReactFabric.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @flow
88
*/
99

10-
import type {ReactFabricType} from './ReactNativeTypes';
10+
import type {ReactFabricType, HostComponent} from './ReactNativeTypes';
1111
import type {ReactNodeList} from 'shared/ReactTypes';
1212

1313
import './ReactFabricInjection';
@@ -44,6 +44,54 @@ import warningWithoutStack from 'shared/warningWithoutStack';
4444

4545
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
4646

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+
4795
function findNodeHandle(componentOrHandle: any): ?number {
4896
if (__DEV__) {
4997
const owner = ReactCurrentOwner.current;
@@ -108,6 +156,9 @@ const roots = new Map();
108156
const ReactFabric: ReactFabricType = {
109157
NativeComponent: ReactNativeComponent(findNodeHandle, findHostInstance),
110158

159+
// This is needed for implementation details of TouchableNativeFeedback
160+
// Remove this once TouchableNativeFeedback doesn't use cloneElement
161+
findHostInstance_deprecated,
111162
findNodeHandle,
112163

113164
dispatchCommand(handle: any, command: string, args: Array<any>) {

packages/react-native-renderer/src/ReactNativeRenderer.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @flow
88
*/
99

10-
import type {ReactNativeType} from './ReactNativeTypes';
10+
import type {ReactNativeType, HostComponent} from './ReactNativeTypes';
1111
import type {ReactNodeList} from 'shared/ReactTypes';
1212

1313
import './ReactNativeInjection';
@@ -47,6 +47,54 @@ import warningWithoutStack from 'shared/warningWithoutStack';
4747

4848
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
4949

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+
5098
function findNodeHandle(componentOrHandle: any): ?number {
5199
if (__DEV__) {
52100
const owner = ReactCurrentOwner.current;
@@ -117,6 +165,9 @@ const roots = new Map();
117165
const ReactNativeRenderer: ReactNativeType = {
118166
NativeComponent: ReactNativeComponent(findNodeHandle, findHostInstance),
119167

168+
// This is needed for implementation details of TouchableNativeFeedback
169+
// Remove this once TouchableNativeFeedback doesn't use cloneElement
170+
findHostInstance_deprecated,
120171
findNodeHandle,
121172

122173
dispatchCommand(handle: any, command: string, args: Array<any>) {

packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,81 @@ describe('ReactFabric', () => {
779779
expect(touchStart2).toBeCalled();
780780
});
781781

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+
782857
it('findNodeHandle should warn if used to find a host component inside StrictMode', () => {
783858
const View = createReactNativeComponentClass('RCTView', () => ({
784859
validAttributes: {foo: true},

packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,26 @@ describe('created with ReactFabric called with ReactNative', () => {
3434
.ReactNativeViewConfigRegistry.register;
3535
});
3636

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+
3757
it('find Fabric nodes with the RN renderer', () => {
3858
const View = createReactNativeComponentClass('RCTView', () => ({
3959
validAttributes: {title: true},
@@ -94,6 +114,26 @@ describe('created with ReactNative called with ReactFabric', () => {
94114
.ReactNativeViewConfigRegistry.register;
95115
});
96116

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+
97137
it('find Paper nodes with the Fabric renderer', () => {
98138
const View = createReactNativeComponentClass('RCTView', () => ({
99139
validAttributes: {title: true},

packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,81 @@ describe('ReactNative', () => {
541541
);
542542
});
543543

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+
544619
it('findNodeHandle should warn if used to find a host component inside StrictMode', () => {
545620
const View = createReactNativeComponentClass('RCTView', () => ({
546621
validAttributes: {foo: true},

0 commit comments

Comments
 (0)