Skip to content

Commit 62b2a85

Browse files
authored
Merge pull request #2041 from reduxjs/nullable-context
2 parents 5a406a9 + 9455c49 commit 62b2a85

16 files changed

+72
-67
lines changed

docs/api/Provider.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ interface ProviderProps<A extends Action = AnyAction, S = any> {
3939
* to create a context to be used.
4040
* If this is used, you'll need to customize `connect` by supplying the same
4141
* context provided to the Provider.
42-
* Initial value doesn't matter, as it is overwritten with the internal state of Provider.
42+
* Set the initial value to null, and the hooks will error
43+
* if this is not overwritten by Provider.
4344
*/
44-
context?: Context<ReactReduxContextValue<S, A>>
45+
context?: Context<ReactReduxContextValue<S, A> | null>
4546

4647
/** Global configuration for the `useSelector` stability check */
4748
stabilityCheck?: StabilityCheck

docs/using-react-redux/accessing-store.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Redux store accessible to deeply nested connected components. As of React Redux
2727
by a single default context object instance generated by `React.createContext()`, called `ReactReduxContext`.
2828

2929
React Redux's `<Provider>` component uses `<ReactReduxContext.Provider>` to put the Redux store and the current store
30-
state into context, and `connect` uses `<ReactReduxContext.Consumer>` to read those values and handle updates.
30+
state into context, and `connect` uses `useContext(ReactReduxContext)` to read those values and handle updates.
3131

3232
## Using the `useStore` Hook
3333

@@ -87,8 +87,8 @@ This also provides a natural isolation of the stores as they live in separate co
8787

8888
```js
8989
// a naive example
90-
const ContextA = React.createContext();
91-
const ContextB = React.createContext();
90+
const ContextA = React.createContext(null);
91+
const ContextB = React.createContext(null);
9292

9393
// assuming reducerA and reducerB are proper reducer functions
9494
const storeA = createStore(reducerA);

src/components/Context.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,26 @@ const ContextKey = Symbol.for(`react-redux-context`)
1919
const gT: {
2020
[ContextKey]?: Map<
2121
typeof React.createContext,
22-
Context<ReactReduxContextValue>
22+
Context<ReactReduxContextValue | null>
2323
>
24-
} = (typeof globalThis !== "undefined" ? globalThis : /* fall back to a per-module scope (pre-8.1 behaviour) if `globalThis` is not available */ {}) as any;
24+
} = (
25+
typeof globalThis !== 'undefined'
26+
? globalThis
27+
: /* fall back to a per-module scope (pre-8.1 behaviour) if `globalThis` is not available */ {}
28+
) as any
2529

26-
function getContext(): Context<ReactReduxContextValue> {
30+
function getContext(): Context<ReactReduxContextValue | null> {
2731
if (!React.createContext) return {} as any
2832

2933
const contextMap = (gT[ContextKey] ??= new Map<
3034
typeof React.createContext,
31-
Context<ReactReduxContextValue>
35+
Context<ReactReduxContextValue | null>
3236
>())
3337
let realContext = contextMap.get(React.createContext)
3438
if (!realContext) {
35-
realContext = React.createContext<ReactReduxContextValue>(null as any)
39+
realContext = React.createContext<ReactReduxContextValue | null>(
40+
null as any
41+
)
3642
if (process.env.NODE_ENV !== 'production') {
3743
realContext.displayName = 'ReactRedux'
3844
}

src/components/Provider.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ export interface ProviderProps<
2424
/**
2525
* Optional context to be used internally in react-redux. Use React.createContext() to create a context to be used.
2626
* If this is used, you'll need to customize `connect` by supplying the same context provided to the Provider.
27-
* Initial value doesn't matter, as it is overwritten with the internal state of Provider.
27+
* Set the initial value to null, and the hooks will error
28+
* if this is not overwritten by Provider.
2829
*/
29-
context?: Context<ReactReduxContextValue<S, A>>
30+
context?: Context<ReactReduxContextValue<S, A> | null>
3031

3132
/** Global configuration for the `useSelector` stability check */
3233
stabilityCheck?: CheckFrequency

src/components/connect.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ function connect<
584584
: contextValue!.store
585585

586586
const getServerState = didStoreComeFromContext
587-
? contextValue.getServerState
587+
? contextValue!.getServerState
588588
: store.getState
589589

590590
const childPropsSelector = React.useMemo(() => {

src/hooks/useDispatch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function createDispatchHook<
1515
S = unknown,
1616
A extends Action<string> = UnknownAction
1717
// @ts-ignore
18-
>(context?: Context<ReactReduxContextValue<S, A>> = ReactReduxContext) {
18+
>(context?: Context<ReactReduxContextValue<S, A> | null> = ReactReduxContext) {
1919
const useStore =
2020
// @ts-ignore
2121
context === ReactReduxContext ? useDefaultStore : createStoreHook(context)

src/hooks/useReduxContext.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type { ReactReduxContextValue } from '../components/Context'
1010
* @returns {Function} A `useReduxContext` hook bound to the specified context.
1111
*/
1212
export function createReduxContextHook(context = ReactReduxContext) {
13-
return function useReduxContext(): ReactReduxContextValue | null {
13+
return function useReduxContext(): ReactReduxContextValue {
1414
const contextValue = React.useContext(context)
1515

1616
if (process.env.NODE_ENV !== 'production' && !contextValue) {
@@ -19,7 +19,7 @@ export function createReduxContextHook(context = ReactReduxContext) {
1919
)
2020
}
2121

22-
return contextValue
22+
return contextValue!
2323
}
2424
}
2525

src/hooks/useSelector.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ const refEquality: EqualityFn<any> = (a, b) => a === b
4343
* @returns {Function} A `useSelector` hook bound to the specified context.
4444
*/
4545
export function createSelectorHook(
46-
context: React.Context<ReactReduxContextValue<any, any>> = ReactReduxContext
46+
context: React.Context<ReactReduxContextValue<
47+
any,
48+
any
49+
> | null> = ReactReduxContext
4750
): UseSelector {
4851
const useReduxContext =
4952
context === ReactReduxContext
@@ -83,7 +86,7 @@ export function createSelectorHook(
8386
getServerState,
8487
stabilityCheck: globalStabilityCheck,
8588
noopCheck: globalNoopCheck,
86-
} = useReduxContext()!
89+
} = useReduxContext()
8790

8891
const firstRun = React.useRef(true)
8992

src/hooks/useStore.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function createStoreHook<
1717
S = unknown,
1818
A extends BasicAction = UnknownAction
1919
// @ts-ignore
20-
>(context?: Context<ReactReduxContextValue<S, A>> = ReactReduxContext) {
20+
>(context?: Context<ReactReduxContextValue<S, A> | null> = ReactReduxContext) {
2121
const useReduxContext =
2222
// @ts-ignore
2323
context === ReactReduxContext
@@ -29,7 +29,7 @@ export function createStoreHook<
2929
Action2 extends BasicAction = A
3030
// @ts-ignore
3131
>() {
32-
const { store } = useReduxContext()!
32+
const { store } = useReduxContext()
3333
// @ts-ignore
3434
return store as Store<State, Action2>
3535
}

test/components/connect.spec.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,9 +2130,10 @@ describe('React', () => {
21302130
}
21312131
}
21322132

2133-
const context = React.createContext<
2134-
ReactReduxContextValue<any, AnyAction>
2135-
>(null as any)
2133+
const context = React.createContext<ReactReduxContextValue<
2134+
any,
2135+
AnyAction
2136+
> | null>(null)
21362137

21372138
let actualState
21382139

@@ -2171,9 +2172,10 @@ describe('React', () => {
21712172
}
21722173
}
21732174

2174-
const context = React.createContext<
2175-
ReactReduxContextValue<any, AnyAction>
2176-
>(null as any)
2175+
const context = React.createContext<ReactReduxContextValue<
2176+
any,
2177+
AnyAction
2178+
> | null>(null)
21772179

21782180
let actualState
21792181

@@ -2421,9 +2423,8 @@ describe('React', () => {
24212423
(state: RootStateType = 0, action: ActionType) =>
24222424
action.type === 'INC' ? state + 1 : state
24232425
)
2424-
const customContext = React.createContext<ReactReduxContextValue>(
2425-
null as any
2426-
)
2426+
const customContext =
2427+
React.createContext<ReactReduxContextValue | null>(null)
24272428

24282429
class A extends Component {
24292430
render() {

0 commit comments

Comments
 (0)