Warn when second callback is passed to setState/dispatch in Hooks (#14625)

This commit is contained in:
Dan Abramov
2019-01-18 18:48:58 +00:00
committed by GitHub
parent d17d0b99c1
commit 177fb76353
2 changed files with 73 additions and 0 deletions
+9
View File
@@ -782,6 +782,15 @@ function dispatchAction<S, A>(
'an infinite loop.',
);
if (__DEV__) {
warning(
arguments.length <= 3,
"State updates from the useState() and useReducer() Hooks don't support the " +
'second callback argument. To execute a side effect after ' +
'rendering, declare it in the component body with useEffect().',
);
}
const alternate = fiber.alternate;
if (
fiber === currentlyRenderingFiber ||
@@ -183,6 +183,70 @@ describe('ReactHooks', () => {
expect(root).toFlushAndYield(['Parent: 1, 2 (dark)']);
});
it('warns about setState second argument', () => {
const {useState} = React;
let setCounter;
function Counter() {
const [counter, _setCounter] = useState(0);
setCounter = _setCounter;
ReactTestRenderer.unstable_yield(`Count: ${counter}`);
return counter;
}
const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
root.update(<Counter />);
expect(root).toFlushAndYield(['Count: 0']);
expect(root).toMatchRenderedOutput('0');
expect(() => {
setCounter(1, () => {
throw new Error('Expected to ignore the callback.');
});
}).toWarnDev(
'State updates from the useState() and useReducer() Hooks ' +
"don't support the second callback argument. " +
'To execute a side effect after rendering, ' +
'declare it in the component body with useEffect().',
{withoutStack: true},
);
expect(root).toFlushAndYield(['Count: 1']);
expect(root).toMatchRenderedOutput('1');
});
it('warns about dispatch second argument', () => {
const {useReducer} = React;
let dispatch;
function Counter() {
const [counter, _dispatch] = useReducer((s, a) => a, 0);
dispatch = _dispatch;
ReactTestRenderer.unstable_yield(`Count: ${counter}`);
return counter;
}
const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
root.update(<Counter />);
expect(root).toFlushAndYield(['Count: 0']);
expect(root).toMatchRenderedOutput('0');
expect(() => {
dispatch(1, () => {
throw new Error('Expected to ignore the callback.');
});
}).toWarnDev(
'State updates from the useState() and useReducer() Hooks ' +
"don't support the second callback argument. " +
'To execute a side effect after rendering, ' +
'declare it in the component body with useEffect().',
{withoutStack: true},
);
expect(root).toFlushAndYield(['Count: 1']);
expect(root).toMatchRenderedOutput('1');
});
it('never bails out if context has changed', () => {
const {useState, useLayoutEffect, useContext} = React;