Skip to content

Commit 3294a5e

Browse files
committed
Add a pair of tests for removing the parent
1 parent 8ce0a24 commit 3294a5e

File tree

1 file changed

+118
-2
lines changed

1 file changed

+118
-2
lines changed

packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,7 +1632,7 @@ describe('ReactLazy', () => {
16321632
expect(root).toMatchRenderedOutput('ba');
16331633
});
16341634

1635-
it('does not destroy layout effects twice', async () => {
1635+
it('does not destroy layout effects twice when hidden child is removed', async () => {
16361636
function ChildA({label}) {
16371637
React.useLayoutEffect(() => {
16381638
Scheduler.unstable_yieldValue('Did mount: ' + label);
@@ -1686,7 +1686,7 @@ describe('ReactLazy', () => {
16861686
expect(root).toMatchRenderedOutput('B');
16871687
});
16881688

1689-
it('does not call componentWillUnmount twice', async () => {
1689+
it('does not call componentWillUnmount twice when hidden child is removed', async () => {
16901690
class ChildA extends React.Component {
16911691
componentDidMount() {
16921692
Scheduler.unstable_yieldValue('Did mount: ' + this.props.label);
@@ -1743,4 +1743,120 @@ describe('ReactLazy', () => {
17431743
expect(Scheduler).toFlushAndYield(['B', 'Did mount: B']);
17441744
expect(root).toMatchRenderedOutput('B');
17451745
});
1746+
1747+
it('does not destroy layout effects twice when parent suspense is removed', async () => {
1748+
function ChildA({label}) {
1749+
React.useLayoutEffect(() => {
1750+
Scheduler.unstable_yieldValue('Did mount: ' + label);
1751+
return () => {
1752+
Scheduler.unstable_yieldValue('Will unmount: ' + label);
1753+
};
1754+
}, []);
1755+
return <Text text={label} />;
1756+
}
1757+
function ChildB({label}) {
1758+
React.useLayoutEffect(() => {
1759+
Scheduler.unstable_yieldValue('Did mount: ' + label);
1760+
return () => {
1761+
Scheduler.unstable_yieldValue('Will unmount: ' + label);
1762+
};
1763+
}, []);
1764+
return <Text text={label} />;
1765+
}
1766+
const LazyChildA = lazy(() => fakeImport(ChildA));
1767+
const LazyChildB = lazy(() => fakeImport(ChildB));
1768+
1769+
function Parent({swap}) {
1770+
return (
1771+
<Suspense fallback={<Text text="Loading..." />}>
1772+
{swap ? <LazyChildB label="B" /> : <LazyChildA label="A" />}
1773+
</Suspense>
1774+
);
1775+
}
1776+
1777+
const root = ReactTestRenderer.create(<Parent swap={false} />, {
1778+
unstable_isConcurrent: true,
1779+
});
1780+
1781+
expect(Scheduler).toFlushAndYield(['Loading...']);
1782+
1783+
await LazyChildA;
1784+
expect(Scheduler).toFlushAndYield(['A', 'Did mount: A']);
1785+
expect(root).toMatchRenderedOutput('A');
1786+
1787+
// Swap the position of A and B
1788+
root.unstable_flushSync(() => {
1789+
root.update(<Parent swap={true} />);
1790+
});
1791+
expect(Scheduler).toHaveYielded(['Loading...', 'Will unmount: A']);
1792+
expect(root).toMatchRenderedOutput('Loading...');
1793+
1794+
// Destroy the whole tree, including the hidden A
1795+
root.unstable_flushSync(() => {
1796+
root.update(<h1>Hello</h1>);
1797+
});
1798+
expect(Scheduler).toFlushAndYield([]);
1799+
expect(root).toMatchRenderedOutput(<h1>Hello</h1>);
1800+
});
1801+
1802+
it('does not call componentWillUnmount twice when parent suspense is removed', async () => {
1803+
class ChildA extends React.Component {
1804+
componentDidMount() {
1805+
Scheduler.unstable_yieldValue('Did mount: ' + this.props.label);
1806+
}
1807+
componentWillUnmount() {
1808+
Scheduler.unstable_yieldValue('Will unmount: ' + this.props.label);
1809+
}
1810+
render() {
1811+
return <Text text={this.props.label} />;
1812+
}
1813+
}
1814+
1815+
class ChildB extends React.Component {
1816+
componentDidMount() {
1817+
Scheduler.unstable_yieldValue('Did mount: ' + this.props.label);
1818+
}
1819+
componentWillUnmount() {
1820+
Scheduler.unstable_yieldValue('Will unmount: ' + this.props.label);
1821+
}
1822+
render() {
1823+
return <Text text={this.props.label} />;
1824+
}
1825+
}
1826+
1827+
const LazyChildA = lazy(() => fakeImport(ChildA));
1828+
const LazyChildB = lazy(() => fakeImport(ChildB));
1829+
1830+
function Parent({swap}) {
1831+
return (
1832+
<Suspense fallback={<Text text="Loading..." />}>
1833+
{swap ? <LazyChildB label="B" /> : <LazyChildA label="A" />}
1834+
</Suspense>
1835+
);
1836+
}
1837+
1838+
const root = ReactTestRenderer.create(<Parent swap={false} />, {
1839+
unstable_isConcurrent: true,
1840+
});
1841+
1842+
expect(Scheduler).toFlushAndYield(['Loading...']);
1843+
1844+
await LazyChildA;
1845+
expect(Scheduler).toFlushAndYield(['A', 'Did mount: A']);
1846+
expect(root).toMatchRenderedOutput('A');
1847+
1848+
// Swap the position of A and B
1849+
root.unstable_flushSync(() => {
1850+
root.update(<Parent swap={true} />);
1851+
});
1852+
expect(Scheduler).toHaveYielded(['Loading...', 'Will unmount: A']);
1853+
expect(root).toMatchRenderedOutput('Loading...');
1854+
1855+
// Destroy the whole tree, including the hidden A
1856+
root.unstable_flushSync(() => {
1857+
root.update(<h1>Hello</h1>);
1858+
});
1859+
expect(Scheduler).toFlushAndYield(['']);
1860+
expect(root).toMatchRenderedOutput(<h1>Hello</h1>);
1861+
});
17461862
});

0 commit comments

Comments
 (0)