Skip to content

Commit e30c669

Browse files
authored
[Fiber] Delete isMounted internals (facebook#31966)
The public API has been deleted a long time ago so this should be unused unless it's used by hacks. It should be replaced with an effect/lifecycle that manually tracks this if you need it. The problem with this API is how the timing implemented because it requires Placement/Hydration flags to be cleared too early. In fact, that's why we also have a separate PlacementDEV flag that works differently. https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberCommitWork.js#L2157-L2165 We should be able to remove this code now.
1 parent 379089d commit e30c669

File tree

6 files changed

+0
-316
lines changed

6 files changed

+0
-316
lines changed

packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js

-180
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
let act;
1313

1414
let React;
15-
let ReactDOM;
1615
let ReactDOMClient;
1716
let assertConsoleErrorDev;
1817
let assertConsoleWarnDev;
@@ -63,24 +62,6 @@ const POST_WILL_UNMOUNT_STATE = {
6362
hasWillUnmountCompleted: true,
6463
};
6564

66-
/**
67-
* Every React component is in one of these life cycles.
68-
*/
69-
type ComponentLifeCycle =
70-
/**
71-
* Mounted components have a DOM node representation and are capable of
72-
* receiving new props.
73-
*/
74-
| 'MOUNTED'
75-
/**
76-
* Unmounted components are inactive and cannot receive new props.
77-
*/
78-
| 'UNMOUNTED';
79-
80-
function getLifeCycleState(instance): ComponentLifeCycle {
81-
return instance.updater.isMounted(instance) ? 'MOUNTED' : 'UNMOUNTED';
82-
}
83-
8465
/**
8566
* TODO: We should make any setState calls fail in
8667
* `getInitialState` and `componentWillMount`. They will usually fail
@@ -99,7 +80,6 @@ describe('ReactComponentLifeCycle', () => {
9980
} = require('internal-test-utils'));
10081

10182
React = require('react');
102-
ReactDOM = require('react-dom');
10383
ReactDOMClient = require('react-dom/client');
10484
});
10585

@@ -290,137 +270,6 @@ describe('ReactComponentLifeCycle', () => {
290270
});
291271
});
292272

293-
it('should correctly determine if a component is mounted', async () => {
294-
class Component extends React.Component {
295-
_isMounted() {
296-
// No longer a public API, but we can test that it works internally by
297-
// reaching into the updater.
298-
return this.updater.isMounted(this);
299-
}
300-
UNSAFE_componentWillMount() {
301-
expect(this._isMounted()).toBeFalsy();
302-
}
303-
componentDidMount() {
304-
expect(this._isMounted()).toBeTruthy();
305-
}
306-
render() {
307-
expect(this._isMounted()).toBeFalsy();
308-
return <div />;
309-
}
310-
}
311-
312-
let instance;
313-
const element = <Component ref={current => (instance = current)} />;
314-
315-
const container = document.createElement('div');
316-
const root = ReactDOMClient.createRoot(container);
317-
await act(() => {
318-
root.render(element);
319-
});
320-
assertConsoleErrorDev([
321-
'Component is accessing isMounted inside its render() function. ' +
322-
'render() should be a pure function of props and state. ' +
323-
'It should never access something that requires stale data ' +
324-
'from the previous render, such as refs. ' +
325-
'Move this logic to componentDidMount and componentDidUpdate instead.\n' +
326-
' in Component (at **)',
327-
]);
328-
expect(instance._isMounted()).toBeTruthy();
329-
});
330-
331-
it('should correctly determine if a null component is mounted', async () => {
332-
class Component extends React.Component {
333-
_isMounted() {
334-
// No longer a public API, but we can test that it works internally by
335-
// reaching into the updater.
336-
return this.updater.isMounted(this);
337-
}
338-
UNSAFE_componentWillMount() {
339-
expect(this._isMounted()).toBeFalsy();
340-
}
341-
componentDidMount() {
342-
expect(this._isMounted()).toBeTruthy();
343-
}
344-
render() {
345-
expect(this._isMounted()).toBeFalsy();
346-
return null;
347-
}
348-
}
349-
350-
let instance;
351-
const element = <Component ref={current => (instance = current)} />;
352-
353-
const container = document.createElement('div');
354-
const root = ReactDOMClient.createRoot(container);
355-
await act(() => {
356-
root.render(element);
357-
});
358-
assertConsoleErrorDev([
359-
'Component is accessing isMounted inside its render() function. ' +
360-
'render() should be a pure function of props and state. ' +
361-
'It should never access something that requires stale data ' +
362-
'from the previous render, such as refs. ' +
363-
'Move this logic to componentDidMount and componentDidUpdate instead.\n' +
364-
' in Component (at **)',
365-
]);
366-
expect(instance._isMounted()).toBeTruthy();
367-
});
368-
369-
it('isMounted should return false when unmounted', async () => {
370-
class Component extends React.Component {
371-
render() {
372-
return <div />;
373-
}
374-
}
375-
376-
const root = ReactDOMClient.createRoot(document.createElement('div'));
377-
const instanceRef = React.createRef();
378-
await act(() => {
379-
root.render(<Component ref={instanceRef} />);
380-
});
381-
const instance = instanceRef.current;
382-
383-
// No longer a public API, but we can test that it works internally by
384-
// reaching into the updater.
385-
expect(instance.updater.isMounted(instance)).toBe(true);
386-
387-
await act(() => {
388-
root.unmount();
389-
});
390-
391-
expect(instance.updater.isMounted(instance)).toBe(false);
392-
});
393-
394-
// @gate www && classic
395-
it('warns if legacy findDOMNode is used inside render', async () => {
396-
class Component extends React.Component {
397-
state = {isMounted: false};
398-
componentDidMount() {
399-
this.setState({isMounted: true});
400-
}
401-
render() {
402-
if (this.state.isMounted) {
403-
expect(ReactDOM.findDOMNode(this).tagName).toBe('DIV');
404-
}
405-
return <div />;
406-
}
407-
}
408-
409-
const container = document.createElement('div');
410-
const root = ReactDOMClient.createRoot(container);
411-
await act(() => {
412-
root.render(<Component />);
413-
});
414-
assertConsoleErrorDev([
415-
'Component is accessing findDOMNode inside its render(). ' +
416-
'render() should be a pure function of props and state. ' +
417-
'It should never access something that requires stale data ' +
418-
'from the previous render, such as refs. ' +
419-
'Move this logic to componentDidMount and componentDidUpdate instead.\n' +
420-
' in Component (at **)',
421-
]);
422-
});
423-
424273
it('should carry through each of the phases of setup', async () => {
425274
class LifeCycleComponent extends React.Component {
426275
constructor(props, context) {
@@ -433,31 +282,25 @@ describe('ReactComponentLifeCycle', () => {
433282
hasWillUnmountCompleted: false,
434283
};
435284
this._testJournal.returnedFromGetInitialState = clone(initState);
436-
this._testJournal.lifeCycleAtStartOfGetInitialState =
437-
getLifeCycleState(this);
438285
this.state = initState;
439286
}
440287

441288
UNSAFE_componentWillMount() {
442289
this._testJournal.stateAtStartOfWillMount = clone(this.state);
443-
this._testJournal.lifeCycleAtStartOfWillMount = getLifeCycleState(this);
444290
this.state.hasWillMountCompleted = true;
445291
}
446292

447293
componentDidMount() {
448294
this._testJournal.stateAtStartOfDidMount = clone(this.state);
449-
this._testJournal.lifeCycleAtStartOfDidMount = getLifeCycleState(this);
450295
this.setState({hasDidMountCompleted: true});
451296
}
452297

453298
render() {
454299
const isInitialRender = !this.state.hasRenderCompleted;
455300
if (isInitialRender) {
456301
this._testJournal.stateInInitialRender = clone(this.state);
457-
this._testJournal.lifeCycleInInitialRender = getLifeCycleState(this);
458302
} else {
459303
this._testJournal.stateInLaterRender = clone(this.state);
460-
this._testJournal.lifeCycleInLaterRender = getLifeCycleState(this);
461304
}
462305
// you would *NEVER* do anything like this in real code!
463306
this.state.hasRenderCompleted = true;
@@ -466,8 +309,6 @@ describe('ReactComponentLifeCycle', () => {
466309

467310
componentWillUnmount() {
468311
this._testJournal.stateAtStartOfWillUnmount = clone(this.state);
469-
this._testJournal.lifeCycleAtStartOfWillUnmount =
470-
getLifeCycleState(this);
471312
this.state.hasWillUnmountCompleted = true;
472313
}
473314
}
@@ -480,52 +321,33 @@ describe('ReactComponentLifeCycle', () => {
480321
await act(() => {
481322
root.render(<LifeCycleComponent ref={instanceRef} />);
482323
});
483-
assertConsoleErrorDev([
484-
'LifeCycleComponent is accessing isMounted inside its render() function. ' +
485-
'render() should be a pure function of props and state. ' +
486-
'It should never access something that requires stale data ' +
487-
'from the previous render, such as refs. ' +
488-
'Move this logic to componentDidMount and componentDidUpdate instead.\n' +
489-
' in LifeCycleComponent (at **)',
490-
]);
491324
const instance = instanceRef.current;
492325

493326
// getInitialState
494327
expect(instance._testJournal.returnedFromGetInitialState).toEqual(
495328
GET_INIT_STATE_RETURN_VAL,
496329
);
497-
expect(instance._testJournal.lifeCycleAtStartOfGetInitialState).toBe(
498-
'UNMOUNTED',
499-
);
500330

501331
// componentWillMount
502332
expect(instance._testJournal.stateAtStartOfWillMount).toEqual(
503333
instance._testJournal.returnedFromGetInitialState,
504334
);
505-
expect(instance._testJournal.lifeCycleAtStartOfWillMount).toBe('UNMOUNTED');
506335

507336
// componentDidMount
508337
expect(instance._testJournal.stateAtStartOfDidMount).toEqual(
509338
DID_MOUNT_STATE,
510339
);
511-
expect(instance._testJournal.lifeCycleAtStartOfDidMount).toBe('MOUNTED');
512340

513341
// initial render
514342
expect(instance._testJournal.stateInInitialRender).toEqual(
515343
INIT_RENDER_STATE,
516344
);
517-
expect(instance._testJournal.lifeCycleInInitialRender).toBe('UNMOUNTED');
518-
519-
expect(getLifeCycleState(instance)).toBe('MOUNTED');
520345

521346
// Now *update the component*
522347
instance.forceUpdate();
523348

524349
// render 2nd time
525350
expect(instance._testJournal.stateInLaterRender).toEqual(NEXT_RENDER_STATE);
526-
expect(instance._testJournal.lifeCycleInLaterRender).toBe('MOUNTED');
527-
528-
expect(getLifeCycleState(instance)).toBe('MOUNTED');
529351

530352
await act(() => {
531353
root.unmount();
@@ -535,10 +357,8 @@ describe('ReactComponentLifeCycle', () => {
535357
WILL_UNMOUNT_STATE,
536358
);
537359
// componentWillUnmount called right before unmount.
538-
expect(instance._testJournal.lifeCycleAtStartOfWillUnmount).toBe('MOUNTED');
539360

540361
// But the current lifecycle of the component is unmounted.
541-
expect(getLifeCycleState(instance)).toBe('UNMOUNTED');
542362
expect(instance.state).toEqual(POST_WILL_UNMOUNT_STATE);
543363
});
544364

packages/react-reconciler/src/ReactFiberClassComponent.js

-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
disableDefaultPropsExceptForClasses,
2424
} from 'shared/ReactFeatureFlags';
2525
import ReactStrictModeWarnings from './ReactStrictModeWarnings';
26-
import {isMounted} from './ReactFiberTreeReflection';
2726
import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap';
2827
import shallowEqual from 'shared/shallowEqual';
2928
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
@@ -165,7 +164,6 @@ function applyDerivedStateFromProps(
165164
}
166165

167166
const classComponentUpdater = {
168-
isMounted,
169167
// $FlowFixMe[missing-local-annot]
170168
enqueueSetState(inst: any, payload: any, callback) {
171169
const fiber = getInstance(inst);

packages/react-reconciler/src/ReactFiberContext.js

-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import type {Fiber} from './ReactInternalTypes';
1111
import type {StackCursor} from './ReactFiberStack';
1212

13-
import {isFiberMounted} from './ReactFiberTreeReflection';
1413
import {disableLegacyContext} from 'shared/ReactFeatureFlags';
1514
import {ClassComponent, HostRoot} from './ReactWorkTags';
1615
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
@@ -285,13 +284,6 @@ function findCurrentUnmaskedContext(fiber: Fiber): Object {
285284
} else {
286285
// Currently this is only used with renderSubtreeIntoContainer; not sure if it
287286
// makes sense elsewhere
288-
if (!isFiberMounted(fiber) || fiber.tag !== ClassComponent) {
289-
throw new Error(
290-
'Expected subtree parent to be a mounted class component. ' +
291-
'This error is likely caused by a bug in React. Please file an issue.',
292-
);
293-
}
294-
295287
let node: Fiber = fiber;
296288
do {
297289
switch (node.tag) {

packages/react-reconciler/src/ReactFiberTreeReflection.js

-35
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ import type {Fiber} from './ReactInternalTypes';
1111
import type {Container, SuspenseInstance} from './ReactFiberConfig';
1212
import type {SuspenseState} from './ReactFiberSuspenseComponent';
1313

14-
import {get as getInstance} from 'shared/ReactInstanceMap';
15-
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
1614
import {
17-
ClassComponent,
1815
HostComponent,
1916
HostHoistable,
2017
HostSingleton,
@@ -24,7 +21,6 @@ import {
2421
SuspenseComponent,
2522
} from './ReactWorkTags';
2623
import {NoFlags, Placement, Hydrating} from './ReactFiberFlags';
27-
import {current as currentOwner, isRendering} from './ReactCurrentFiber';
2824

2925
export function getNearestMountedFiber(fiber: Fiber): null | Fiber {
3026
let node = fiber;
@@ -83,37 +79,6 @@ export function getContainerFromFiber(fiber: Fiber): null | Container {
8379
: null;
8480
}
8581

86-
export function isFiberMounted(fiber: Fiber): boolean {
87-
return getNearestMountedFiber(fiber) === fiber;
88-
}
89-
90-
export function isMounted(component: React$Component<any, any>): boolean {
91-
if (__DEV__) {
92-
const owner = currentOwner;
93-
if (owner !== null && isRendering && owner.tag === ClassComponent) {
94-
const ownerFiber: Fiber = owner;
95-
const instance = ownerFiber.stateNode;
96-
if (!instance._warnedAboutRefsInRender) {
97-
console.error(
98-
'%s is accessing isMounted inside its render() function. ' +
99-
'render() should be a pure function of props and state. It should ' +
100-
'never access something that requires stale data from the previous ' +
101-
'render, such as refs. Move this logic to componentDidMount and ' +
102-
'componentDidUpdate instead.',
103-
getComponentNameFromFiber(ownerFiber) || 'A component',
104-
);
105-
}
106-
instance._warnedAboutRefsInRender = true;
107-
}
108-
}
109-
110-
const fiber: ?Fiber = getInstance(component);
111-
if (!fiber) {
112-
return false;
113-
}
114-
return getNearestMountedFiber(fiber) === fiber;
115-
}
116-
11782
function assertIsMounted(fiber: Fiber) {
11883
if (getNearestMountedFiber(fiber) !== fiber) {
11984
throw new Error('Unable to find node on an unmounted component.');

0 commit comments

Comments
 (0)