diff --git a/packages/react-reconciler/src/ReactUpdateQueue.js b/packages/react-reconciler/src/ReactUpdateQueue.js index aafc453426089..3652b3c25c537 100644 --- a/packages/react-reconciler/src/ReactUpdateQueue.js +++ b/packages/react-reconciler/src/ReactUpdateQueue.js @@ -88,6 +88,10 @@ import type {Fiber} from './ReactFiber'; import type {ExpirationTime} from './ReactFiberExpirationTime'; import {NoWork} from './ReactFiberExpirationTime'; +import { + stashContextDependenciesInDEV, + unstashContextDependenciesInDEV, +} from './ReactFiberNewContext'; import {Callback, ShouldCapture, DidCapture} from 'shared/ReactSideEffectTags'; import {ClassComponent} from 'shared/ReactWorkTags'; @@ -348,6 +352,7 @@ function getStateFromUpdate( if (typeof payload === 'function') { // Updater function if (__DEV__) { + stashContextDependenciesInDEV(); if ( debugRenderPhaseSideEffects || (debugRenderPhaseSideEffectsForStrictMode && @@ -356,7 +361,11 @@ function getStateFromUpdate( payload.call(instance, prevState, nextProps); } } - return payload.call(instance, prevState, nextProps); + const nextState = payload.call(instance, prevState, nextProps); + if (__DEV__) { + unstashContextDependenciesInDEV(); + } + return nextState; } // State object return payload; @@ -372,6 +381,7 @@ function getStateFromUpdate( if (typeof payload === 'function') { // Updater function if (__DEV__) { + stashContextDependenciesInDEV(); if ( debugRenderPhaseSideEffects || (debugRenderPhaseSideEffectsForStrictMode && @@ -381,6 +391,9 @@ function getStateFromUpdate( } } partialState = payload.call(instance, prevState, nextProps); + if (__DEV__) { + unstashContextDependenciesInDEV(); + } } else { // Partial state object partialState = payload; diff --git a/packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js b/packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js index 3b9873923a173..b81cc5d588257 100644 --- a/packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js @@ -1347,6 +1347,31 @@ describe('ReactNewContext', () => { expect(ReactNoop.flush()).toEqual(['App', 'App#renderConsumer']); expect(ReactNoop.getChildren()).toEqual([span('goodbye')]); }); + + it('warns when reading context inside render phase class setState updater', () => { + const ThemeContext = React.createContext('light'); + + class Cls extends React.Component { + state = {}; + render() { + this.setState(() => { + readContext(ThemeContext); + }); + return null; + } + } + + ReactNoop.render(); + expect(ReactNoop.flush).toWarnDev( + [ + 'Context can only be read while React is rendering', + 'Cannot update during an existing state transition', + ], + { + withoutStack: 1, + }, + ); + }); }); describe('useContext', () => {