@@ -672,6 +672,147 @@ describe('ReactHooks', () => {
672672 expect ( root . toJSON ( ) ) . toEqual ( '123' ) ;
673673 } ) ;
674674
675+ it ( 'throws when reading context inside useMemo' , ( ) => {
676+ const { useMemo, createContext} = React ;
677+ const ReactCurrentDispatcher =
678+ React . __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
679+ . ReactCurrentDispatcher ;
680+
681+ const ThemeContext = createContext ( 'light' ) ;
682+ function App ( ) {
683+ return useMemo ( ( ) => {
684+ return ReactCurrentDispatcher . current . readContext ( ThemeContext ) ;
685+ } , [ ] ) ;
686+ }
687+
688+ expect ( ( ) => ReactTestRenderer . create ( < App /> ) ) . toThrow (
689+ 'Context can only be read while React is rendering' ,
690+ ) ;
691+ } ) ;
692+
693+ it ( 'throws when reading context inside useMemo after reading outside it' , ( ) => {
694+ const { useMemo, createContext} = React ;
695+ const ReactCurrentDispatcher =
696+ React . __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
697+ . ReactCurrentDispatcher ;
698+
699+ const ThemeContext = createContext ( 'light' ) ;
700+ let firstRead , secondRead ;
701+ function App ( ) {
702+ firstRead = ReactCurrentDispatcher . current . readContext ( ThemeContext ) ;
703+ useMemo ( ( ) => { } ) ;
704+ secondRead = ReactCurrentDispatcher . current . readContext ( ThemeContext ) ;
705+ return useMemo ( ( ) => {
706+ return ReactCurrentDispatcher . current . readContext ( ThemeContext ) ;
707+ } , [ ] ) ;
708+ }
709+
710+ expect ( ( ) => ReactTestRenderer . create ( < App /> ) ) . toThrow (
711+ 'Context can only be read while React is rendering' ,
712+ ) ;
713+ expect ( firstRead ) . toBe ( 'light' ) ;
714+ expect ( secondRead ) . toBe ( 'light' ) ;
715+ } ) ;
716+
717+ it ( 'throws when reading context inside useEffect' , ( ) => {
718+ const { useEffect, createContext} = React ;
719+ const ReactCurrentDispatcher =
720+ React . __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
721+ . ReactCurrentDispatcher ;
722+
723+ const ThemeContext = createContext ( 'light' ) ;
724+ function App ( ) {
725+ useEffect ( ( ) => {
726+ ReactCurrentDispatcher . current . readContext ( ThemeContext ) ;
727+ } ) ;
728+ return null ;
729+ }
730+
731+ const root = ReactTestRenderer . create ( < App /> ) ;
732+ expect ( ( ) => root . update ( < App /> ) ) . toThrow (
733+ // The exact message doesn't matter, just make sure we don't allow this
734+ "Cannot read property 'readContext' of null" ,
735+ ) ;
736+ } ) ;
737+
738+ it ( 'throws when reading context inside useLayoutEffect' , ( ) => {
739+ const { useLayoutEffect, createContext} = React ;
740+ const ReactCurrentDispatcher =
741+ React . __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
742+ . ReactCurrentDispatcher ;
743+
744+ const ThemeContext = createContext ( 'light' ) ;
745+ function App ( ) {
746+ useLayoutEffect ( ( ) => {
747+ ReactCurrentDispatcher . current . readContext ( ThemeContext ) ;
748+ } ) ;
749+ return null ;
750+ }
751+
752+ expect ( ( ) => ReactTestRenderer . create ( < App /> ) ) . toThrow (
753+ // The exact message doesn't matter, just make sure we don't allow this
754+ "Cannot read property 'readContext' of null" ,
755+ ) ;
756+ } ) ;
757+
758+ it ( 'throws when reading context inside useReducer' , ( ) => {
759+ const { useReducer, createContext} = React ;
760+ const ReactCurrentDispatcher =
761+ React . __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
762+ . ReactCurrentDispatcher ;
763+
764+ const ThemeContext = createContext ( 'light' ) ;
765+ function App ( ) {
766+ useReducer (
767+ ( ) => {
768+ ReactCurrentDispatcher . current . readContext ( ThemeContext ) ;
769+ } ,
770+ null ,
771+ { } ,
772+ ) ;
773+ return null ;
774+ }
775+
776+ expect ( ( ) => ReactTestRenderer . create ( < App /> ) ) . toThrow (
777+ 'Context can only be read while React is rendering' ,
778+ ) ;
779+ } ) ;
780+
781+ // Edge case.
782+ it ( 'throws when reading context inside eager useReducer' , ( ) => {
783+ const { useState, createContext} = React ;
784+ const ThemeContext = createContext ( 'light' ) ;
785+
786+ const ReactCurrentDispatcher =
787+ React . __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
788+ . ReactCurrentDispatcher ;
789+
790+ let _setState ;
791+ function Fn ( ) {
792+ const [ , setState ] = useState ( 0 ) ;
793+ _setState = setState ;
794+ return null ;
795+ }
796+
797+ class Cls extends React . Component {
798+ render ( ) {
799+ _setState ( ( ) => {
800+ ReactCurrentDispatcher . current . readContext ( ThemeContext ) ;
801+ } ) ;
802+ return null ;
803+ }
804+ }
805+
806+ expect ( ( ) =>
807+ ReactTestRenderer . create (
808+ < React . Fragment >
809+ < Fn />
810+ < Cls />
811+ </ React . Fragment > ,
812+ ) ,
813+ ) . toThrow ( 'Context can only be read while React is rendering' ) ;
814+ } ) ;
815+
675816 it ( 'throws when calling hooks inside useReducer' , ( ) => {
676817 const { useReducer, useRef} = React ;
677818 function App ( ) {
0 commit comments