Skip to content

Redux 5alpha5 #3367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/toolkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
],
"dependencies": {
"immer": "^10.0.0-beta.4",
"redux": "5.0.0-alpha.4",
"redux": "5.0.0-alpha.5",
"redux-thunk": "3.0.0-alpha.3",
"reselect": "^4.1.7"
},
Expand Down
29 changes: 10 additions & 19 deletions packages/toolkit/src/configureStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import type {
StoreEnhancer,
Store,
Dispatch,
PreloadedState,
CombinedState,
} from 'redux'
import { createStore, compose, applyMiddleware, combineReducers } from 'redux'
import type { DevToolsEnhancerOptions as DevToolsOptions } from './devtoolsExtension'
Expand All @@ -21,7 +19,6 @@ import type {
} from './getDefaultMiddleware'
import { curryGetDefaultMiddleware } from './getDefaultMiddleware'
import type {
NoInfer,
ExtractDispatchExtensions,
ExtractStoreExtensions,
} from './tsHelpers'
Expand All @@ -46,13 +43,14 @@ export interface ConfigureStoreOptions<
S = any,
A extends Action = AnyAction,
M extends Middlewares<S> = Middlewares<S>,
E extends Enhancers = Enhancers
E extends Enhancers = Enhancers,
P = S
> {
/**
* A single reducer function that will be used as the root reducer, or an
* object of slice reducers that will be passed to `combineReducers()`.
*/
reducer: Reducer<S, A> | ReducersMapObject<S, A>
reducer: Reducer<S, A, P> | ReducersMapObject<S, A, P>

/**
* An array of Redux middleware to install. If not supplied, defaults to
Expand All @@ -78,16 +76,8 @@ export interface ConfigureStoreOptions<
* function (either directly or indirectly by passing an object as `reducer`),
* this must be an object with the same shape as the reducer map keys.
*/
/*
Not 100% correct but the best approximation we can get:
- if S is a `CombinedState` applying a second `CombinedState` on it does not change anything.
- if it is not, there could be two cases:
- `ReducersMapObject<S, A>` is being passed in. In this case, we will call `combineReducers` on it and `CombinedState<S>` is correct
- `Reducer<S, A>` is being passed in. In this case, actually `CombinedState<S>` is wrong and `S` would be correct.
As we cannot distinguish between those two cases without adding another generic parameter,
we just make the pragmatic assumption that the latter almost never happens.
*/
preloadedState?: PreloadedState<CombinedState<NoInfer<S>>>
// we infer here, and instead complain if the reducer doesn't match
preloadedState?: P

/**
* The store enhancers to apply. See Redux's `createStore()`.
Expand Down Expand Up @@ -142,8 +132,9 @@ export function configureStore<
S = any,
A extends Action = AnyAction,
M extends Middlewares<S> = [ThunkMiddlewareFor<S>],
E extends Enhancers = [StoreEnhancer]
>(options: ConfigureStoreOptions<S, A, M, E>): EnhancedStore<S, A, M, E> {
E extends Enhancers = [StoreEnhancer],
P = S
>(options: ConfigureStoreOptions<S, A, M, E, P>): EnhancedStore<S, A, M, E> {
const curriedGetDefaultMiddleware = curryGetDefaultMiddleware<S>()

const {
Expand All @@ -154,12 +145,12 @@ export function configureStore<
enhancers = undefined,
} = options || {}

let rootReducer: Reducer<S, A>
let rootReducer: Reducer<S, A, P>

if (typeof reducer === 'function') {
rootReducer = reducer
} else if (isPlainObject(reducer)) {
rootReducer = combineReducers(reducer) as unknown as Reducer<S, A>
rootReducer = combineReducers(reducer) as unknown as Reducer<S, A, P>
} else {
throw new Error(
'"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers'
Expand Down
8 changes: 6 additions & 2 deletions packages/toolkit/src/createAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,15 +286,19 @@ export function createAction(type: string, prepareAction?: Function): any {
return actionCreator
}

export function isAction(action: unknown): action is Action<unknown> {
return isPlainObject(action) && 'type' in action
}

export function isFSA(action: unknown): action is {
type: string
payload?: unknown
error?: unknown
meta?: unknown
} {
return (
isPlainObject(action) &&
typeof (action as any).type === 'string' &&
isAction(action) &&
typeof action.type === 'string' &&
Object.keys(action).every(isValidKey)
)
}
Expand Down
2 changes: 2 additions & 0 deletions packages/toolkit/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export {
// js
createAction,
getType,
isAction,
isFSA as isFluxStandardAction,
} from './createAction'
export type {
// types
Expand Down
7 changes: 6 additions & 1 deletion packages/toolkit/src/listenerMiddleware/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Dispatch, AnyAction, MiddlewareAPI } from 'redux'
import type { ThunkDispatch } from 'redux-thunk'
import { createAction } from '../createAction'
import { createAction, isAction } from '../createAction'
import { nanoid } from '../nanoid'

import type {
Expand Down Expand Up @@ -426,6 +426,11 @@ export function createListenerMiddleware<

const middleware: ListenerMiddleware<S, D, ExtraArgument> =
(api) => (next) => (action) => {
if (!isAction(action)) {
// this means that the listeners can't react to anything that doesn't look like an action (plain object with .type property)
// but that matches the typing, so i think that's fine?
return next(action)
}
if (addListener.match(action)) {
return startListening(action.payload)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/toolkit/src/listenerMiddleware/tests/fork.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ describe('fork', () => {
},
})

store.dispatch(increment)
store.dispatch(increment())

expect(await deferredResult).toBe(listenerCompleted)
})
Expand Down
5 changes: 4 additions & 1 deletion packages/toolkit/src/query/core/buildMiddleware/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AnyAction, Middleware, ThunkDispatch } from '@reduxjs/toolkit'
import { createAction } from '@reduxjs/toolkit'
import { isAction, createAction } from '@reduxjs/toolkit'

import type {
EndpointDefinitions,
Expand Down Expand Up @@ -80,6 +80,9 @@ export function buildMiddleware<

return (next) => {
return (action) => {
if (!isAction(action)) {
return next(action)
}
if (!initialized) {
initialized = true
// dispatch before any other action
Expand Down
8 changes: 5 additions & 3 deletions packages/toolkit/src/query/core/buildMiddleware/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,13 @@ export type SubMiddlewareBuilder = (
ThunkDispatch<any, any, AnyAction>
>

export type ApiMiddlewareInternalHandler<ReturnType = void> = (
type MwNext = Parameters<ReturnType<Middleware>>[0]

export type ApiMiddlewareInternalHandler<Return = void> = (
action: AnyAction,
mwApi: SubMiddlewareApi & { next: Dispatch<AnyAction> },
mwApi: SubMiddlewareApi & { next: MwNext },
prevState: RootState<EndpointDefinitions, string, string>
) => ReturnType
) => Return

export type InternalHandlerBuilder<ReturnType = void> = (
input: BuildSubMiddlewareInput
Expand Down
5 changes: 1 addition & 4 deletions packages/toolkit/src/query/core/buildSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
prepareAutoBatched,
} from '@reduxjs/toolkit'
import type {
CombinedState as CombinedQueryState,
QuerySubstateIdentifier,
QuerySubState,
MutationSubstateIdentifier,
Expand Down Expand Up @@ -469,9 +468,7 @@ export function buildSlice({
},
})

const combinedReducer = combineReducers<
CombinedQueryState<any, string, string>
>({
const combinedReducer = combineReducers({
queries: querySlice.reducer,
mutations: mutationSlice.reducer,
provided: invalidationSlice.reducer,
Expand Down
10 changes: 9 additions & 1 deletion packages/toolkit/src/serializableStateInvariantMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import isPlainObject from './isPlainObject'
import type { Middleware } from 'redux'
import { getTimeMeasureUtils } from './utils'
import { isAction } from './createAction'

/**
* Returns true if the passed value is "plain", i.e. a value that is either
Expand Down Expand Up @@ -207,6 +208,10 @@ export function createSerializableStateInvariantMiddleware(
!disableCache && WeakSet ? new WeakSet() : undefined

return (storeAPI) => (next) => (action) => {
if (!isAction(action)) {
return next(action)
}

const result = next(action)

const measureUtils = getTimeMeasureUtils(
Expand All @@ -216,7 +221,10 @@ export function createSerializableStateInvariantMiddleware(

if (
!ignoreActions &&
!(ignoredActions.length && ignoredActions.indexOf(action.type) !== -1)
!(
ignoredActions.length &&
ignoredActions.indexOf(action.type as any) !== -1
)
) {
measureUtils.measureTime(() => {
const foundActionNonSerializableValue = findNonSerializableValue(
Expand Down
Loading