@samitouri / QOS-React-2 / commits / 177fb76353

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

Dan Abramov committed Jan 18, 2019 at 18:48 UTC 177fb7635391fb9c56ba3b8e38fd45131b73ebe7
2 files changed +73
packages/react-reconciler/src/ReactFiberHooks.js
+9
@@ -782,6 +782,15 @@ function dispatchAction<S, A>(
782 'an infinite loop.',
783 );
784
785 + if (__DEV__) {
786 + warning(
787 + arguments.length <= 3,
788 + "State updates from the useState() and useReducer() Hooks don't support the " +
789 + 'second callback argument. To execute a side effect after ' +
790 + 'rendering, declare it in the component body with useEffect().',
791 + );
792 + }
793 +
794 const alternate = fiber.alternate;
795 if (
796 fiber === currentlyRenderingFiber ||
packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
+64
@@ -183,6 +183,70 @@ describe('ReactHooks', () => {
183 expect(root).toFlushAndYield(['Parent: 1, 2 (dark)']);
184 });
185
186 + it('warns about setState second argument', () => {
187 + const {useState} = React;
188 +
189 + let setCounter;
190 + function Counter() {
191 + const [counter, _setCounter] = useState(0);
192 + setCounter = _setCounter;
193 +
194 + ReactTestRenderer.unstable_yield(`Count: ${counter}`);
195 + return counter;
196 + }
197 +
198 + const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
199 + root.update(<Counter />);
200 + expect(root).toFlushAndYield(['Count: 0']);
201 + expect(root).toMatchRenderedOutput('0');
202 +
203 + expect(() => {
204 + setCounter(1, () => {
205 + throw new Error('Expected to ignore the callback.');
206 + });
207 + }).toWarnDev(
208 + 'State updates from the useState() and useReducer() Hooks ' +
209 + "don't support the second callback argument. " +
210 + 'To execute a side effect after rendering, ' +
211 + 'declare it in the component body with useEffect().',
212 + {withoutStack: true},
213 + );
214 + expect(root).toFlushAndYield(['Count: 1']);
215 + expect(root).toMatchRenderedOutput('1');
216 + });
217 +
218 + it('warns about dispatch second argument', () => {
219 + const {useReducer} = React;
220 +
221 + let dispatch;
222 + function Counter() {
223 + const [counter, _dispatch] = useReducer((s, a) => a, 0);
224 + dispatch = _dispatch;
225 +
226 + ReactTestRenderer.unstable_yield(`Count: ${counter}`);
227 + return counter;
228 + }
229 +
230 + const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
231 + root.update(<Counter />);
232 + expect(root).toFlushAndYield(['Count: 0']);
233 + expect(root).toMatchRenderedOutput('0');
234 +
235 + expect(() => {
236 + dispatch(1, () => {
237 + throw new Error('Expected to ignore the callback.');
238 + });
239 + }).toWarnDev(
240 + 'State updates from the useState() and useReducer() Hooks ' +
241 + "don't support the second callback argument. " +
242 + 'To execute a side effect after rendering, ' +
243 + 'declare it in the component body with useEffect().',
244 + {withoutStack: true},
245 + );
246 + expect(root).toFlushAndYield(['Count: 1']);
247 + expect(root).toMatchRenderedOutput('1');
248 + });
249 +
250 it('never bails out if context has changed', () => {
251 const {useState, useLayoutEffect, useContext} = React;
252