diff --git a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js index 75e4cebe80610..f985d06597673 100644 --- a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js +++ b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js @@ -12,7 +12,6 @@ let act; let React; -let ReactDOM; let ReactDOMClient; let assertConsoleErrorDev; let assertConsoleWarnDev; @@ -63,24 +62,6 @@ const POST_WILL_UNMOUNT_STATE = { hasWillUnmountCompleted: true, }; -/** - * Every React component is in one of these life cycles. - */ -type ComponentLifeCycle = - /** - * Mounted components have a DOM node representation and are capable of - * receiving new props. - */ - | 'MOUNTED' - /** - * Unmounted components are inactive and cannot receive new props. - */ - | 'UNMOUNTED'; - -function getLifeCycleState(instance): ComponentLifeCycle { - return instance.updater.isMounted(instance) ? 'MOUNTED' : 'UNMOUNTED'; -} - /** * TODO: We should make any setState calls fail in * `getInitialState` and `componentWillMount`. They will usually fail @@ -99,7 +80,6 @@ describe('ReactComponentLifeCycle', () => { } = require('internal-test-utils')); React = require('react'); - ReactDOM = require('react-dom'); ReactDOMClient = require('react-dom/client'); }); @@ -290,137 +270,6 @@ describe('ReactComponentLifeCycle', () => { }); }); - it('should correctly determine if a component is mounted', async () => { - class Component extends React.Component { - _isMounted() { - // No longer a public API, but we can test that it works internally by - // reaching into the updater. - return this.updater.isMounted(this); - } - UNSAFE_componentWillMount() { - expect(this._isMounted()).toBeFalsy(); - } - componentDidMount() { - expect(this._isMounted()).toBeTruthy(); - } - render() { - expect(this._isMounted()).toBeFalsy(); - return
; - } - } - - let instance; - const element = (instance = current)} />; - - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - await act(() => { - root.render(element); - }); - assertConsoleErrorDev([ - 'Component is accessing isMounted inside its render() function. ' + - 'render() should be a pure function of props and state. ' + - 'It should never access something that requires stale data ' + - 'from the previous render, such as refs. ' + - 'Move this logic to componentDidMount and componentDidUpdate instead.\n' + - ' in Component (at **)', - ]); - expect(instance._isMounted()).toBeTruthy(); - }); - - it('should correctly determine if a null component is mounted', async () => { - class Component extends React.Component { - _isMounted() { - // No longer a public API, but we can test that it works internally by - // reaching into the updater. - return this.updater.isMounted(this); - } - UNSAFE_componentWillMount() { - expect(this._isMounted()).toBeFalsy(); - } - componentDidMount() { - expect(this._isMounted()).toBeTruthy(); - } - render() { - expect(this._isMounted()).toBeFalsy(); - return null; - } - } - - let instance; - const element = (instance = current)} />; - - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - await act(() => { - root.render(element); - }); - assertConsoleErrorDev([ - 'Component is accessing isMounted inside its render() function. ' + - 'render() should be a pure function of props and state. ' + - 'It should never access something that requires stale data ' + - 'from the previous render, such as refs. ' + - 'Move this logic to componentDidMount and componentDidUpdate instead.\n' + - ' in Component (at **)', - ]); - expect(instance._isMounted()).toBeTruthy(); - }); - - it('isMounted should return false when unmounted', async () => { - class Component extends React.Component { - render() { - return
; - } - } - - const root = ReactDOMClient.createRoot(document.createElement('div')); - const instanceRef = React.createRef(); - await act(() => { - root.render(); - }); - const instance = instanceRef.current; - - // No longer a public API, but we can test that it works internally by - // reaching into the updater. - expect(instance.updater.isMounted(instance)).toBe(true); - - await act(() => { - root.unmount(); - }); - - expect(instance.updater.isMounted(instance)).toBe(false); - }); - - // @gate www && classic - it('warns if legacy findDOMNode is used inside render', async () => { - class Component extends React.Component { - state = {isMounted: false}; - componentDidMount() { - this.setState({isMounted: true}); - } - render() { - if (this.state.isMounted) { - expect(ReactDOM.findDOMNode(this).tagName).toBe('DIV'); - } - return
; - } - } - - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - await act(() => { - root.render(); - }); - assertConsoleErrorDev([ - 'Component is accessing findDOMNode inside its render(). ' + - 'render() should be a pure function of props and state. ' + - 'It should never access something that requires stale data ' + - 'from the previous render, such as refs. ' + - 'Move this logic to componentDidMount and componentDidUpdate instead.\n' + - ' in Component (at **)', - ]); - }); - it('should carry through each of the phases of setup', async () => { class LifeCycleComponent extends React.Component { constructor(props, context) { @@ -433,20 +282,16 @@ describe('ReactComponentLifeCycle', () => { hasWillUnmountCompleted: false, }; this._testJournal.returnedFromGetInitialState = clone(initState); - this._testJournal.lifeCycleAtStartOfGetInitialState = - getLifeCycleState(this); this.state = initState; } UNSAFE_componentWillMount() { this._testJournal.stateAtStartOfWillMount = clone(this.state); - this._testJournal.lifeCycleAtStartOfWillMount = getLifeCycleState(this); this.state.hasWillMountCompleted = true; } componentDidMount() { this._testJournal.stateAtStartOfDidMount = clone(this.state); - this._testJournal.lifeCycleAtStartOfDidMount = getLifeCycleState(this); this.setState({hasDidMountCompleted: true}); } @@ -454,10 +299,8 @@ describe('ReactComponentLifeCycle', () => { const isInitialRender = !this.state.hasRenderCompleted; if (isInitialRender) { this._testJournal.stateInInitialRender = clone(this.state); - this._testJournal.lifeCycleInInitialRender = getLifeCycleState(this); } else { this._testJournal.stateInLaterRender = clone(this.state); - this._testJournal.lifeCycleInLaterRender = getLifeCycleState(this); } // you would *NEVER* do anything like this in real code! this.state.hasRenderCompleted = true; @@ -466,8 +309,6 @@ describe('ReactComponentLifeCycle', () => { componentWillUnmount() { this._testJournal.stateAtStartOfWillUnmount = clone(this.state); - this._testJournal.lifeCycleAtStartOfWillUnmount = - getLifeCycleState(this); this.state.hasWillUnmountCompleted = true; } } @@ -480,52 +321,33 @@ describe('ReactComponentLifeCycle', () => { await act(() => { root.render(); }); - assertConsoleErrorDev([ - 'LifeCycleComponent is accessing isMounted inside its render() function. ' + - 'render() should be a pure function of props and state. ' + - 'It should never access something that requires stale data ' + - 'from the previous render, such as refs. ' + - 'Move this logic to componentDidMount and componentDidUpdate instead.\n' + - ' in LifeCycleComponent (at **)', - ]); const instance = instanceRef.current; // getInitialState expect(instance._testJournal.returnedFromGetInitialState).toEqual( GET_INIT_STATE_RETURN_VAL, ); - expect(instance._testJournal.lifeCycleAtStartOfGetInitialState).toBe( - 'UNMOUNTED', - ); // componentWillMount expect(instance._testJournal.stateAtStartOfWillMount).toEqual( instance._testJournal.returnedFromGetInitialState, ); - expect(instance._testJournal.lifeCycleAtStartOfWillMount).toBe('UNMOUNTED'); // componentDidMount expect(instance._testJournal.stateAtStartOfDidMount).toEqual( DID_MOUNT_STATE, ); - expect(instance._testJournal.lifeCycleAtStartOfDidMount).toBe('MOUNTED'); // initial render expect(instance._testJournal.stateInInitialRender).toEqual( INIT_RENDER_STATE, ); - expect(instance._testJournal.lifeCycleInInitialRender).toBe('UNMOUNTED'); - - expect(getLifeCycleState(instance)).toBe('MOUNTED'); // Now *update the component* instance.forceUpdate(); // render 2nd time expect(instance._testJournal.stateInLaterRender).toEqual(NEXT_RENDER_STATE); - expect(instance._testJournal.lifeCycleInLaterRender).toBe('MOUNTED'); - - expect(getLifeCycleState(instance)).toBe('MOUNTED'); await act(() => { root.unmount(); @@ -535,10 +357,8 @@ describe('ReactComponentLifeCycle', () => { WILL_UNMOUNT_STATE, ); // componentWillUnmount called right before unmount. - expect(instance._testJournal.lifeCycleAtStartOfWillUnmount).toBe('MOUNTED'); // But the current lifecycle of the component is unmounted. - expect(getLifeCycleState(instance)).toBe('UNMOUNTED'); expect(instance.state).toEqual(POST_WILL_UNMOUNT_STATE); }); diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index d0490b7e02d4e..a5d7ef12ab6ce 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -23,7 +23,6 @@ import { disableDefaultPropsExceptForClasses, } from 'shared/ReactFeatureFlags'; import ReactStrictModeWarnings from './ReactStrictModeWarnings'; -import {isMounted} from './ReactFiberTreeReflection'; import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap'; import shallowEqual from 'shared/shallowEqual'; import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; @@ -165,7 +164,6 @@ function applyDerivedStateFromProps( } const classComponentUpdater = { - isMounted, // $FlowFixMe[missing-local-annot] enqueueSetState(inst: any, payload: any, callback) { const fiber = getInstance(inst); diff --git a/packages/react-reconciler/src/ReactFiberContext.js b/packages/react-reconciler/src/ReactFiberContext.js index 93e9eec922f73..e8a586e856e1d 100644 --- a/packages/react-reconciler/src/ReactFiberContext.js +++ b/packages/react-reconciler/src/ReactFiberContext.js @@ -10,7 +10,6 @@ import type {Fiber} from './ReactInternalTypes'; import type {StackCursor} from './ReactFiberStack'; -import {isFiberMounted} from './ReactFiberTreeReflection'; import {disableLegacyContext} from 'shared/ReactFeatureFlags'; import {ClassComponent, HostRoot} from './ReactWorkTags'; import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; @@ -285,13 +284,6 @@ function findCurrentUnmaskedContext(fiber: Fiber): Object { } else { // Currently this is only used with renderSubtreeIntoContainer; not sure if it // makes sense elsewhere - if (!isFiberMounted(fiber) || fiber.tag !== ClassComponent) { - throw new Error( - 'Expected subtree parent to be a mounted class component. ' + - 'This error is likely caused by a bug in React. Please file an issue.', - ); - } - let node: Fiber = fiber; do { switch (node.tag) { diff --git a/packages/react-reconciler/src/ReactFiberTreeReflection.js b/packages/react-reconciler/src/ReactFiberTreeReflection.js index e0199108693e3..5dce60a559fd8 100644 --- a/packages/react-reconciler/src/ReactFiberTreeReflection.js +++ b/packages/react-reconciler/src/ReactFiberTreeReflection.js @@ -11,10 +11,7 @@ import type {Fiber} from './ReactInternalTypes'; import type {Container, SuspenseInstance} from './ReactFiberConfig'; import type {SuspenseState} from './ReactFiberSuspenseComponent'; -import {get as getInstance} from 'shared/ReactInstanceMap'; -import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; import { - ClassComponent, HostComponent, HostHoistable, HostSingleton, @@ -24,7 +21,6 @@ import { SuspenseComponent, } from './ReactWorkTags'; import {NoFlags, Placement, Hydrating} from './ReactFiberFlags'; -import {current as currentOwner, isRendering} from './ReactCurrentFiber'; export function getNearestMountedFiber(fiber: Fiber): null | Fiber { let node = fiber; @@ -83,37 +79,6 @@ export function getContainerFromFiber(fiber: Fiber): null | Container { : null; } -export function isFiberMounted(fiber: Fiber): boolean { - return getNearestMountedFiber(fiber) === fiber; -} - -export function isMounted(component: React$Component): boolean { - if (__DEV__) { - const owner = currentOwner; - if (owner !== null && isRendering && owner.tag === ClassComponent) { - const ownerFiber: Fiber = owner; - const instance = ownerFiber.stateNode; - if (!instance._warnedAboutRefsInRender) { - console.error( - '%s is accessing isMounted inside its render() function. ' + - 'render() should be a pure function of props and state. It should ' + - 'never access something that requires stale data from the previous ' + - 'render, such as refs. Move this logic to componentDidMount and ' + - 'componentDidUpdate instead.', - getComponentNameFromFiber(ownerFiber) || 'A component', - ); - } - instance._warnedAboutRefsInRender = true; - } - } - - const fiber: ?Fiber = getInstance(component); - if (!fiber) { - return false; - } - return getNearestMountedFiber(fiber) === fiber; -} - function assertIsMounted(fiber: Fiber) { if (getNearestMountedFiber(fiber) !== fiber) { throw new Error('Unable to find node on an unmounted component.'); diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js index cffd690e64715..63ff106bf8432 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js @@ -40,94 +40,6 @@ describe('ReactIncrementalReflection', () => { return {type: 'span', children: [], prop, hidden: false}; } - it('handles isMounted even when the initial render is deferred', async () => { - const instances = []; - - class Component extends React.Component { - _isMounted() { - // No longer a public API, but we can test that it works internally by - // reaching into the updater. - return this.updater.isMounted(this); - } - UNSAFE_componentWillMount() { - instances.push(this); - Scheduler.log('componentWillMount: ' + this._isMounted()); - } - componentDidMount() { - Scheduler.log('componentDidMount: ' + this._isMounted()); - } - render() { - return ; - } - } - - function Foo() { - return ; - } - - React.startTransition(() => { - ReactNoop.render(); - }); - - // Render part way through but don't yet commit the updates. - await waitFor(['componentWillMount: false']); - - expect(instances[0]._isMounted()).toBe(false); - - // Render the rest and commit the updates. - await waitForAll(['componentDidMount: true']); - - expect(instances[0]._isMounted()).toBe(true); - }); - - it('handles isMounted when an unmount is deferred', async () => { - const instances = []; - - class Component extends React.Component { - _isMounted() { - return this.updater.isMounted(this); - } - UNSAFE_componentWillMount() { - instances.push(this); - } - componentWillUnmount() { - Scheduler.log('componentWillUnmount: ' + this._isMounted()); - } - render() { - Scheduler.log('Component'); - return ; - } - } - - function Other() { - Scheduler.log('Other'); - return ; - } - - function Foo(props) { - return props.mount ? : ; - } - - ReactNoop.render(); - await waitForAll(['Component']); - - expect(instances[0]._isMounted()).toBe(true); - - React.startTransition(() => { - ReactNoop.render(); - }); - // Render part way through but don't yet commit the updates so it is not - // fully unmounted yet. - await waitFor(['Other']); - - expect(instances[0]._isMounted()).toBe(true); - - // Finish flushing the unmount. - await waitForAll(['componentWillUnmount: true']); - - expect(instances[0]._isMounted()).toBe(false); - }); - it('finds no node before insertion and correct node before deletion', async () => { let classInstance = null; diff --git a/packages/react-server/src/ReactFizzClassComponent.js b/packages/react-server/src/ReactFizzClassComponent.js index 08b4aaa94c79b..8106b672d64d2 100644 --- a/packages/react-server/src/ReactFizzClassComponent.js +++ b/packages/react-server/src/ReactFizzClassComponent.js @@ -108,9 +108,6 @@ type InternalInstance = { }; const classComponentUpdater = { - isMounted(inst: any) { - return false; - }, // $FlowFixMe[missing-local-annot] enqueueSetState(inst: any, payload: any, callback) { const internals: InternalInstance = getInstance(inst);