Skip to content

Commit

Permalink
Deprecate findDOMNode in StrictMode
Browse files Browse the repository at this point in the history
There are two scenarios. One is that we pass a component instance that is
already in strict mode or the node that we find is in strict mode if
an outer component renders into strict mode.

I use a separate method findHostInstanceWithWarning for this so that
a) I can pass the method name (findDOMNode/findNodeHandle).
b) Can ignore this warning in React Native mixins/NativeComponent that use this helper.

I don't want to expose the fiber to the renderers themselves.
  • Loading branch information
sebmarkbage committed Oct 12, 2018
1 parent 0af8199 commit 26d9a58
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 4 deletions.
68 changes: 67 additions & 1 deletion packages/react-dom/src/__tests__/findDOMNode-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
const React = require('react');
const ReactDOM = require('react-dom');
const ReactTestUtils = require('react-dom/test-utils');
const StrictMode = React.StrictMode;

describe('findDOMNode', () => {
it('findDOMNode should return null if passed null', () => {
Expand Down Expand Up @@ -94,7 +95,72 @@ describe('findDOMNode', () => {
return <div />;
}
}

expect(() => ReactTestUtils.renderIntoDocument(<Bar />)).not.toThrow();
});

it('findDOMNode should warn if used to find a host component inside StrictMode', () => {
let parent = undefined;
let child = undefined;

class ContainsStrictModeChild extends React.Component {
render() {
return (
<StrictMode>
<div ref={n => (child = n)} />
</StrictMode>
);
}
}

ReactTestUtils.renderIntoDocument(
<ContainsStrictModeChild ref={n => (parent = n)} />,
);

let match;
expect(() => (match = ReactDOM.findDOMNode(parent))).toWarnDev([
'Warning: findDOMNode is deprecated in StrictMode. ' +
'findDOMNode was passed an instance of ContainsStrictModeChild which renders a StrictMode subtree. ' +
'The nearest child is in StrictMode. ' +
'Use an explicit ref directly on the element you want to get a handle on.' +
'\n' +
'\n in div (at **)' +
'\n in StrictMode (at **)' +
'\n in ContainsStrictModeChild (at **)' +
'\n' +
'\nLearn more about using refs safely here:' +
'\nhttps://fb.me/react-strict-mode-find-node',
]);
expect(match).toBe(child);
});

it('findDOMNode should warn if passed a component that is inside StrictMode', () => {
let parent = undefined;
let child = undefined;

class IsInStrictMode extends React.Component {
render() {
return <div ref={n => (child = n)} />;
}
}

ReactTestUtils.renderIntoDocument(
<StrictMode>
<IsInStrictMode ref={n => (parent = n)} />
</StrictMode>,
);

let match;
expect(() => (match = ReactDOM.findDOMNode(parent))).toWarnDev([
'Warning: findDOMNode is deprecated in StrictMode. ' +
'findDOMNode was passed an instance of IsInStrictMode which is in a StrictMode subtree. ' +
'Use an explicit ref directly on the element you want to get a handle on.' +
'\n' +
'\n in IsInStrictMode (at **)' +
'\n in StrictMode (at **)' +
'\n' +
'\nLearn more about using refs safely here:' +
'\nhttps://fb.me/react-strict-mode-find-node',
]);
expect(match).toBe(child);
});
});
7 changes: 6 additions & 1 deletion packages/react-dom/src/client/ReactDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,12 @@ const ReactDOM: Object = {
if ((componentOrElement: any).nodeType === ELEMENT_NODE) {
return (componentOrElement: any);
}

if (__DEV__) {
return DOMRenderer.findHostInstanceWithWarning(
componentOrElement,
'findDOMNode',
);
}
return DOMRenderer.findHostInstance(componentOrElement);
},

Expand Down
13 changes: 12 additions & 1 deletion packages/react-native-renderer/src/ReactFabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import warningWithoutStack from 'shared/warningWithoutStack';

const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
const findHostInstance = ReactFabricRenderer.findHostInstance;
const findHostInstanceWithWarning =
ReactFabricRenderer.findHostInstanceWithWarning;

function findNodeHandle(componentOrHandle: any): ?number {
if (__DEV__) {
Expand Down Expand Up @@ -60,7 +62,16 @@ function findNodeHandle(componentOrHandle: any): ?number {
if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) {
return componentOrHandle.canonical._nativeTag;
}
const hostInstance = findHostInstance(componentOrHandle);
let hostInstance;
if (__DEV__) {
hostInstance = findHostInstanceWithWarning(
componentOrHandle,
'findNodeHandle',
);
} else {
hostInstance = findHostInstance(componentOrHandle);
}

if (hostInstance == null) {
return hostInstance;
}
Expand Down
13 changes: 12 additions & 1 deletion packages/react-native-renderer/src/ReactNativeRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import warningWithoutStack from 'shared/warningWithoutStack';

const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
const findHostInstance = ReactNativeFiberRenderer.findHostInstance;
const findHostInstanceWithWarning =
ReactNativeFiberRenderer.findHostInstanceWithWarning;

function findNodeHandle(componentOrHandle: any): ?number {
if (__DEV__) {
Expand Down Expand Up @@ -63,7 +65,16 @@ function findNodeHandle(componentOrHandle: any): ?number {
if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) {
return componentOrHandle.canonical._nativeTag;
}
const hostInstance = findHostInstance(componentOrHandle);
let hostInstance;
if (__DEV__) {
hostInstance = findHostInstanceWithWarning(
componentOrHandle,
'findNodeHandle',
);
} else {
hostInstance = findHostInstance(componentOrHandle);
}

if (hostInstance == null) {
return hostInstance;
}
Expand Down
6 changes: 6 additions & 0 deletions packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,12 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
if (typeof component.id === 'number') {
return component;
}
if (__DEV__) {
return NoopRenderer.findHostInstanceWithWarning(
component,
'findInstance',
);
}
return NoopRenderer.findHostInstance(component);
},

Expand Down
68 changes: 68 additions & 0 deletions packages/react-reconciler/src/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ import {
import {createUpdate, enqueueUpdate} from './ReactUpdateQueue';
import ReactFiberInstrumentation from './ReactFiberInstrumentation';
import * as ReactCurrentFiber from './ReactCurrentFiber';
import {getStackByFiberInDevAndProd} from './ReactCurrentFiber';
import {StrictMode} from './ReactTypeOfMode';

type OpaqueRoot = FiberRoot;

Expand All @@ -82,9 +84,11 @@ type DevToolsConfig = {|
|};

let didWarnAboutNestedUpdates;
let didWarnAboutFindNodeInStrictMode;

if (__DEV__) {
didWarnAboutNestedUpdates = false;
didWarnAboutFindNodeInStrictMode = {};
}

function getContextForSubtree(
Expand Down Expand Up @@ -209,6 +213,68 @@ function findHostInstance(component: Object): PublicInstance | null {
return hostFiber.stateNode;
}

function findHostInstanceWithWarning(
component: Object,
methodName: string,
): PublicInstance | null {
if (__DEV__) {
const fiber = ReactInstanceMap.get(component);
if (fiber === undefined) {
if (typeof component.render === 'function') {
invariant(false, 'Unable to find node on an unmounted component.');
} else {
invariant(
false,
'Argument appears to not be a ReactComponent. Keys: %s',
Object.keys(component),
);
}
}
const hostFiber = findCurrentHostFiber(fiber);
if (hostFiber === null) {
return null;
}
if (hostFiber.mode & StrictMode) {
const componentName = getComponentName(fiber.type) || 'Component';
if (!didWarnAboutFindNodeInStrictMode[componentName]) {
didWarnAboutFindNodeInStrictMode[componentName] = true;
if (fiber.mode & StrictMode) {
warningWithoutStack(
false,
'%s is deprecated in StrictMode. ' +
'%s was passed an instance of %s which is in a StrictMode subtree. ' +
'Use an explicit ref directly on the element you want to get a handle on.' +
'\n%s' +
'\n\nLearn more about using refs safely here:' +
'\nhttps://fb.me/react-strict-mode-find-node',
methodName,
methodName,
componentName,
getStackByFiberInDevAndProd(fiber),
);
} else {
warningWithoutStack(
false,
'%s is deprecated in StrictMode. ' +
'%s was passed an instance of %s which renders a StrictMode subtree. ' +
'The nearest child is in StrictMode. ' +
'Use an explicit ref directly on the element you want to get a handle on.' +
'\n%s' +
'\n\nLearn more about using refs safely here:' +
'\nhttps://fb.me/react-strict-mode-find-node',
methodName,
methodName,
componentName,
getStackByFiberInDevAndProd(hostFiber),
);
}
}
}
return hostFiber.stateNode;
}
return findHostInstance(component);
}

export function createContainer(
containerInfo: Container,
isConcurrent: boolean,
Expand Down Expand Up @@ -266,6 +332,8 @@ export function getPublicRootInstance(

export {findHostInstance};

export {findHostInstanceWithWarning};

export function findHostInstanceWithNoPortals(
fiber: Fiber,
): PublicInstance | null {
Expand Down

0 comments on commit 26d9a58

Please sign in to comment.