Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test bad useEffect return value with noop-renderer #22258

Merged
merged 3 commits into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -727,42 +727,6 @@ describe('ReactHooks', () => {
ReactTestRenderer.create(<App deps={undefined} />);
});

it('assumes useEffect clean-up function is either a function or undefined', () => {
const {useLayoutEffect} = React;

function App(props) {
useLayoutEffect(() => {
return props.return;
});
return null;
}

const root1 = ReactTestRenderer.create(null);
expect(() => root1.update(<App return={17} />)).toErrorDev([
'Warning: An effect function must not return anything besides a ' +
'function, which is used for clean-up. You returned: 17',
]);

const root2 = ReactTestRenderer.create(null);
expect(() => root2.update(<App return={null} />)).toErrorDev([
'Warning: An effect function must not return anything besides a ' +
'function, which is used for clean-up. You returned null. If your ' +
'effect does not require clean up, return undefined (or nothing).',
]);

const root3 = ReactTestRenderer.create(null);
expect(() => root3.update(<App return={Promise.resolve()} />)).toErrorDev([
'Warning: An effect function must not return anything besides a ' +
'function, which is used for clean-up.\n\n' +
'It looks like you wrote useEffect(async () => ...) or returned a Promise.',
]);

// Error on unmount because React assumes the value is a function
expect(() => {
root3.update(null);
}).toThrow('is not a function');
});

it('does not forget render phase useState updates inside an effect', () => {
const {useState, useEffect} = React;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2632,6 +2632,54 @@ describe('ReactHooksWithNoopRenderer', () => {
});
expect(Scheduler).toHaveYielded(['layout destroy', 'passive destroy']);
});

it('assumes passive effect destroy function is either a function or undefined', () => {
function App(props) {
useEffect(() => {
return props.return;
});
return null;
}

const root1 = ReactNoop.createRoot();
expect(() =>
act(() => {
root1.render(<App return={17} />);
}),
).toErrorDev([
'Warning: An effect function must not return anything besides a ' +
'function, which is used for clean-up. You returned: 17',
]);

const root2 = ReactNoop.createRoot();
expect(() =>
act(() => {
root2.render(<App return={null} />);
}),
).toErrorDev([
'Warning: An effect function must not return anything besides a ' +
'function, which is used for clean-up. You returned null. If your ' +
'effect does not require clean up, return undefined (or nothing).',
]);

const root3 = ReactNoop.createRoot();
expect(() =>
act(() => {
root3.render(<App return={Promise.resolve()} />);
}),
).toErrorDev([
'Warning: An effect function must not return anything besides a ' +
'function, which is used for clean-up.\n\n' +
'It looks like you wrote useEffect(async () => ...) or returned a Promise.',
]);

// Error on unmount because React assumes the value is a function
expect(() =>
act(() => {
root3.unmount();
}),
).toThrow('is not a function');
});
});

describe('useLayoutEffect', () => {
Expand Down Expand Up @@ -2810,6 +2858,54 @@ describe('ReactHooksWithNoopRenderer', () => {
]);
expect(ReactNoop.getChildren()).toEqual([span('OuterFallback')]);
});

it('assumes layout effect destroy function is either a function or undefined', () => {
function App(props) {
useLayoutEffect(() => {
return props.return;
});
return null;
}

const root1 = ReactNoop.createRoot();
expect(() =>
act(() => {
root1.render(<App return={17} />);
}),
).toErrorDev([
'Warning: An effect function must not return anything besides a ' +
'function, which is used for clean-up. You returned: 17',
]);

const root2 = ReactNoop.createRoot();
expect(() =>
act(() => {
root2.render(<App return={null} />);
}),
).toErrorDev([
'Warning: An effect function must not return anything besides a ' +
'function, which is used for clean-up. You returned null. If your ' +
'effect does not require clean up, return undefined (or nothing).',
]);

const root3 = ReactNoop.createRoot();
expect(() =>
act(() => {
root3.render(<App return={Promise.resolve()} />);
}),
).toErrorDev([
'Warning: An effect function must not return anything besides a ' +
'function, which is used for clean-up.\n\n' +
'It looks like you wrote useEffect(async () => ...) or returned a Promise.',
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice how the warning never differentiates between wrong useEffect and useLayoutEffect

]);

// Error on unmount because React assumes the value is a function
expect(() =>
act(() => {
root3.unmount();
}),
).toThrow('is not a function');
});
});

describe('useCallback', () => {
Expand Down