diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
index bacf9da7029a7..9bee64a7f9799 100644
--- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
@@ -3847,6 +3847,38 @@ describe('ReactHooksWithNoopRenderer', () => {
// expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 4')]);
});
+ it('mount first state', () => {
+ function App(props) {
+ let A;
+ if (props.loadA) {
+ useState(0);
+ } else {
+ A = '[not loaded]';
+ }
+
+ return ;
+ }
+
+ ReactNoop.render();
+ expect(Scheduler).toFlushAndYield(['A: [not loaded]']);
+ expect(ReactNoop.getChildren()).toEqual([span('A: [not loaded]')]);
+
+ ReactNoop.render();
+ expect(() => {
+ expect(() => {
+ expect(Scheduler).toFlushAndYield(['A: 0']);
+ }).toThrow('Rendered more hooks than during the previous render');
+ }).toErrorDev([
+ 'Warning: React has detected a change in the order of Hooks called by App. ' +
+ 'This will lead to bugs and errors if not fixed. For more information, ' +
+ 'read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n' +
+ ' Previous render Next render\n' +
+ ' ------------------------------------------------------\n' +
+ '3. undefined useState\n' +
+ ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n',
+ ]);
+ });
+
it('unmount state', () => {
let updateA;
let updateB;
@@ -3887,7 +3919,88 @@ describe('ReactHooksWithNoopRenderer', () => {
);
});
- it('unmount effects', () => {
+ it('unmount last state', () => {
+ function App(props) {
+ let A;
+ if (props.loadA) {
+ useState(0);
+ } else {
+ A = '[not loaded]';
+ }
+
+ return ;
+ }
+
+ ReactNoop.render();
+ expect(Scheduler).toFlushAndYield(['A: 0']);
+ expect(ReactNoop.getChildren()).toEqual([span('A: 0')]);
+ ReactNoop.render();
+ expect(Scheduler).toFlushAndThrow(
+ 'Rendered fewer hooks than expected. This may be caused by an ' +
+ 'accidental early return statement.',
+ );
+ });
+
+ it('mount effect', () => {
+ function App(props) {
+ if (props.showMore) {
+ useEffect(() => {
+ Scheduler.unstable_yieldValue('Mount A');
+ return () => {
+ Scheduler.unstable_yieldValue('Unmount A');
+ };
+ }, []);
+ }
+
+ return null;
+ }
+
+ ReactNoop.render();
+ expect(Scheduler).toFlushAndYield([]);
+
+ act(() => {
+ ReactNoop.render();
+ expect(() => {
+ expect(() => {
+ expect(Scheduler).toFlushAndYield([]);
+ }).toThrow('Rendered more hooks than during the previous render');
+ }).toErrorDev([
+ 'Warning: React has detected a change in the order of Hooks called by App. ' +
+ 'This will lead to bugs and errors if not fixed. For more information, ' +
+ 'read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n' +
+ ' Previous render Next render\n' +
+ ' ------------------------------------------------------\n' +
+ '2. undefined useEffect\n' +
+ ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n',
+ ]);
+ });
+ });
+
+ it('unmount effect', () => {
+ function App(props) {
+ if (props.showMore) {
+ useEffect(() => {
+ Scheduler.unstable_yieldValue('Mount A');
+ return () => {
+ Scheduler.unstable_yieldValue('Unmount A');
+ };
+ }, []);
+ }
+
+ return null;
+ }
+
+ ReactNoop.render();
+ expect(Scheduler).toFlushAndYield(['Mount A']);
+
+ ReactNoop.render();
+ expect(Scheduler).toFlushAndThrow(
+ 'Rendered fewer hooks than expected. This may be caused by an ' +
+ 'accidental early return statement.',
+ );
+ });
+
+ it('unmount additional effects', () => {
function App(props) {
useEffect(() => {
Scheduler.unstable_yieldValue('Mount A');