Skip to content

Commit

Permalink
Convert refs-destruction to createRoot (#28011)
Browse files Browse the repository at this point in the history
  • Loading branch information
rickhanlonii authored Jan 24, 2024
1 parent ee1eb48 commit 72411c4
Showing 1 changed file with 69 additions and 38 deletions.
107 changes: 69 additions & 38 deletions packages/react-dom/src/__tests__/refs-destruction-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,22 @@

let React;
let ReactDOM;
let ReactDOMClient;
let ReactTestUtils;

let TestComponent;
let act;
let theInnerDivRef;
let theInnerClassComponentRef;

describe('refs-destruction', () => {
beforeEach(() => {
jest.resetModules();

React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactTestUtils = require('react-dom/test-utils');
act = require('internal-test-utils').act;

class ClassComponent extends React.Component {
render() {
Expand All @@ -30,8 +35,11 @@ describe('refs-destruction', () => {
}

TestComponent = class extends React.Component {
theInnerDivRef = React.createRef();
theInnerClassComponentRef = React.createRef();
constructor(props) {
super(props);
theInnerDivRef = React.createRef();
theInnerClassComponentRef = React.createRef();
}

render() {
if (this.props.destroy) {
Expand All @@ -46,77 +54,96 @@ describe('refs-destruction', () => {
} else {
return (
<div>
<div ref={this.theInnerDivRef} />
<ClassComponent ref={this.theInnerClassComponentRef} />
<div ref={theInnerDivRef} />
<ClassComponent ref={theInnerClassComponentRef} />
</div>
);
}
}
};
});

it('should remove refs when destroying the parent', () => {
afterEach(() => {
theInnerClassComponentRef = null;
theInnerDivRef = null;
});

it('should remove refs when destroying the parent', async () => {
const container = document.createElement('div');
const testInstance = ReactDOM.render(<TestComponent />, container);
const root = ReactDOMClient.createRoot(container);
await act(async () => {
root.render(<TestComponent />);
});

expect(
ReactTestUtils.isDOMComponent(testInstance.theInnerDivRef.current),
).toBe(true);
expect(testInstance.theInnerClassComponentRef.current).toBeTruthy();
expect(ReactTestUtils.isDOMComponent(theInnerDivRef.current)).toBe(true);
expect(theInnerClassComponentRef.current).toBeTruthy();

ReactDOM.unmountComponentAtNode(container);
root.unmount();

expect(testInstance.theInnerDivRef.current).toBe(null);
expect(testInstance.theInnerClassComponentRef.current).toBe(null);
expect(theInnerDivRef.current).toBe(null);
expect(theInnerClassComponentRef.current).toBe(null);
});

it('should remove refs when destroying the child', () => {
it('should remove refs when destroying the child', async () => {
const container = document.createElement('div');
const testInstance = ReactDOM.render(<TestComponent />, container);
expect(
ReactTestUtils.isDOMComponent(testInstance.theInnerDivRef.current),
).toBe(true);
expect(testInstance.theInnerClassComponentRef.current).toBeTruthy();
const root = ReactDOMClient.createRoot(container);
await act(async () => {
root.render(<TestComponent />);
});

expect(ReactTestUtils.isDOMComponent(theInnerDivRef.current)).toBe(true);
expect(theInnerClassComponentRef.current).toBeTruthy();

ReactDOM.render(<TestComponent destroy={true} />, container);
await act(async () => {
root.render(<TestComponent destroy={true} />);
});

expect(testInstance.theInnerDivRef.current).toBe(null);
expect(testInstance.theInnerClassComponentRef.current).toBe(null);
expect(theInnerDivRef.current).toBe(null);
expect(theInnerClassComponentRef.current).toBe(null);
});

it('should remove refs when removing the child ref attribute', () => {
it('should remove refs when removing the child ref attribute', async () => {
const container = document.createElement('div');
const testInstance = ReactDOM.render(<TestComponent />, container);
const root = ReactDOMClient.createRoot(container);
await act(async () => {
root.render(<TestComponent />);
});

expect(
ReactTestUtils.isDOMComponent(testInstance.theInnerDivRef.current),
).toBe(true);
expect(testInstance.theInnerClassComponentRef.current).toBeTruthy();
expect(ReactTestUtils.isDOMComponent(theInnerDivRef.current)).toBe(true);
expect(theInnerClassComponentRef.current).toBeTruthy();

ReactDOM.render(<TestComponent removeRef={true} />, container);
await act(async () => {
root.render(<TestComponent removeRef={true} />);
});

expect(testInstance.theInnerDivRef.current).toBe(null);
expect(testInstance.theInnerClassComponentRef.current).toBe(null);
expect(theInnerDivRef.current).toBe(null);
expect(theInnerClassComponentRef.current).toBe(null);
});

it('should not error when destroying child with ref asynchronously', () => {
it('should not error when destroying child with ref asynchronously', async () => {
let nestedRoot;
class Modal extends React.Component {
componentDidMount() {
this.div = document.createElement('div');
nestedRoot = ReactDOMClient.createRoot(this.div);
document.body.appendChild(this.div);
this.componentDidUpdate();
}

componentDidUpdate() {
ReactDOM.render(<div>{this.props.children}</div>, this.div);
setTimeout(() => {
ReactDOM.flushSync(() => {
nestedRoot.render(<div>{this.props.children}</div>);
});
}, 0);
}

componentWillUnmount() {
const self = this;
// some async animation
setTimeout(function () {
expect(function () {
ReactDOM.unmountComponentAtNode(self.div);
nestedRoot.unmount();
}).not.toThrow();
document.body.removeChild(self.div);
}, 0);
Expand Down Expand Up @@ -144,8 +171,12 @@ describe('refs-destruction', () => {
}

const container = document.createElement('div');
ReactDOM.render(<App />, container);
ReactDOM.render(<App hidden={true} />, container);
jest.runAllTimers();
const root = ReactDOMClient.createRoot(container);
await act(async () => {
root.render(<App />);
});
await act(async () => {
root.render(<App hidden={true} />);
});
});
});

0 comments on commit 72411c4

Please sign in to comment.