mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Warn when second callback is passed to setState/dispatch in Hooks (#14625)
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user