@@ -672,6 +672,147 @@ describe('ReactHooks', () => {
672
672
expect ( root . toJSON ( ) ) . toEqual ( '123' ) ;
673
673
} ) ;
674
674
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
+
675
816
it ( 'throws when calling hooks inside useReducer' , ( ) => {
676
817
const { useReducer, useRef} = React ;
677
818
function App ( ) {
0 commit comments