①configureStore関数の実装
node_modules/@reduxjs/toolkit/src/configureStore.ts
/** 116行目 */
export function configureStore<
S = any,
A extends Action = UnknownAction,
M extends Tuple<Middlewares<S>> = Tuple<[ThunkMiddlewareFor<S>]>,
E extends Tuple<Enhancers> = Tuple<
[StoreEnhancer<{ dispatch: ExtractDispatchExtensions<M> }>, StoreEnhancer]
>,
P = S,
>(options: ConfigureStoreOptions<S, A, M, E, P>): EnhancedStore<S, A, E> {
const getDefaultMiddleware = buildGetDefaultMiddleware<S>()
const {
reducer = undefined,
middleware,
devTools = true,
preloadedState = undefined,
enhancers = undefined,
} = options || {}
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, 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',
)
}
/** 以下略 */
以下の部分でcombineReducers関数を呼び出し、Reducerの初期化を行っています。
node_modules/@reduxjs/toolkit/src/configureStore.ts
} else if (isPlainObject(reducer)) {
rootReducer = combineReducers(reducer) as unknown as Reducer<S, A, P>
}
以下でcombineReducers関数をimportしており、combineReducers関数がreduxライブラリの関数であることが確認できます。
node_modules/@reduxjs/toolkit/src/configureStore.ts
/** 1行目 */
import type {
Reducer,
ReducersMapObject,
Middleware,
Action,
StoreEnhancer,
Store,
UnknownAction,
} from 'redux'
import {
applyMiddleware,
createStore,
compose,
combineReducers,
isPlainObject,
} from 'redux'
/** 以下略 */
②combineReducers関数の実装
node_modules/redux/src/combineReducers.ts
/** 123行目 */
export default function combineReducers(reducers: {
[key: string]: Reducer<any, any, any>
}) {
const reducerKeys = Object.keys(reducers)
const finalReducers: { [key: string]: Reducer<any, any, any> } = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
warning(`No reducer provided for key "${key}"`)
}
}
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
// This is used to make sure we don't warn about the same
// keys multiple times.
let unexpectedKeyCache: { [key: string]: true }
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
let shapeAssertionError: unknown
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
/** 以下略 */
以下の部分でassertReducerShape関数を呼び出し、Reducerの初期化を行っています。
node_modules/@reduxjs/toolkit/src/configureStore.ts
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
以下でassertReducerShape関数を定義しています。
node_modules/redux/src/combineReducers.ts
/** 62行目 */
function assertReducerShape(reducers: {
[key: string]: Reducer<any, any, any>
}) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key]
const initialState = reducer(undefined, { type: ActionTypes.INIT })
if (typeof initialState === 'undefined') {
throw new Error(
`The slice reducer for key "${key}" returned undefined during initialization. ` +
`If the state passed to the reducer is undefined, you must ` +
`explicitly return the initial state. The initial state may ` +
`not be undefined. If you don't want to set a value for this reducer, ` +
`you can use null instead of undefined.`
)
}
if (
typeof reducer(undefined, {
type: ActionTypes.PROBE_UNKNOWN_ACTION()
}) === 'undefined'
) {
throw new Error(
`The slice reducer for key "${key}" returned undefined when probed with a random type. ` +
`Don't try to handle '${ActionTypes.INIT}' or other actions in "redux/*" ` +
`namespace. They are considered private. Instead, you must return the ` +
`current state for any unknown actions, unless it is undefined, ` +
`in which case you must return the initial state, regardless of the ` +
`action type. The initial state may not be undefined, but can be null.`
)
}
})
}
/** 以下略 */
下記の通り、オブジェクト内のreducerに対し、init処理を行っていることが確認できます。
node_modules/redux/src/combineReducers.ts
Object.keys(reducers).forEach(key => {
const reducer = reducers[key]
const initialState = reducer(undefined, { type: ActionTypes.INIT })
おわりに
storeにreducerを渡しただけで使えるようになる、というredux-toolkitの仕様にやや困惑したので、ソースをさかのぼって確認してみました。
自分の目ではソースを読み切るのが難しく、LLMと一緒に作業してみました。いい経験になりました!