diff --git a/packages/react-dom/src/__tests__/ReactUpdates-test.js b/packages/react-dom/src/__tests__/ReactUpdates-test.js
index 1b86a59db3310..d37f4dbb66054 100644
--- a/packages/react-dom/src/__tests__/ReactUpdates-test.js
+++ b/packages/react-dom/src/__tests__/ReactUpdates-test.js
@@ -876,13 +876,9 @@ describe('ReactUpdates', () => {
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: [object Object]',
);
+ // Make sure the warning is deduplicated and doesn't fire again
component = ReactTestUtils.renderIntoDocument();
- expect(() => {
- expect(() => component.setState({}, new Foo())).toWarnDev(
- 'setState(...): Expected the last optional `callback` argument to be ' +
- 'a function. Instead received: [object Object].',
- );
- }).toThrowError(
+ expect(() => component.setState({}, new Foo())).toThrowError(
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: [object Object]',
);
@@ -923,13 +919,9 @@ describe('ReactUpdates', () => {
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: [object Object]',
);
+ // Make sure the warning is deduplicated and doesn't fire again
component = ReactTestUtils.renderIntoDocument();
- expect(() => {
- expect(() => component.forceUpdate(new Foo())).toWarnDev(
- 'forceUpdate(...): Expected the last optional `callback` argument to be ' +
- 'a function. Instead received: [object Object].',
- );
- }).toThrowError(
+ expect(() => component.forceUpdate(new Foo())).toThrowError(
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: [object Object]',
);
diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js
index 75ddf38169941..b5fcbd60f5bbe 100644
--- a/packages/react-reconciler/src/ReactFiberClassComponent.js
+++ b/packages/react-reconciler/src/ReactFiberClassComponent.js
@@ -44,16 +44,24 @@ let didWarnAboutStateAssignmentForComponent;
let warnOnInvalidCallback;
if (__DEV__) {
+ const didWarnOnInvalidCallback = {};
didWarnAboutStateAssignmentForComponent = {};
warnOnInvalidCallback = function(callback: mixed, callerName: string) {
- warning(
- callback === null || typeof callback === 'function',
- '%s(...): Expected the last optional `callback` argument to be a ' +
- 'function. Instead received: %s.',
- callerName,
- callback,
- );
+ if (callback === null || typeof callback === 'function') {
+ return;
+ }
+ const key = `${callerName}_${(callback: any)}`;
+ if (!didWarnOnInvalidCallback[key]) {
+ warning(
+ false,
+ '%s(...): Expected the last optional `callback` argument to be a ' +
+ 'function. Instead received: %s.',
+ callerName,
+ callback,
+ );
+ didWarnOnInvalidCallback[key] = true;
+ }
};
// This is so gross but it's at least non-critical and can be removed if