Why redux calls all reducers every time an action is dispatched ? Meanwhile mostly in real world app we just want to update a specific state in the store with a specific reducer ?
See original GitHub issueIs it good for performance when a large app has over 100 reducers, when it calls 100 reducers with 99 redundant calls.
As I read from the source code, whenever dispatch an action with a specific type to the store, it will call the combination reducer function and loop through all over the reducers and call each one to find the matching action type which time complexity is O(n). But in real world app, for myself, I mostly just need to update a specific state in the store at a specific reducer and with specific action.
Here is a snippet of code in combineReducers.ts
return function combination(
state: StateFromReducersMapObject<typeof reducers> = {},
action: AnyAction
) {
if (shapeAssertionError) {
throw shapeAssertionError
}
if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(
state,
finalReducers,
action,
unexpectedKeyCache
)
if (warningMessage) {
warning(warningMessage)
}
}
let hasChanged = false
const nextState: StateFromReducersMapObject<typeof reducers> = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
hasChanged =
hasChanged || finalReducerKeys.length !== Object.keys(state).length
return hasChanged ? nextState : state
}
So I do some experiment in the code, when dispatching an action, I add one more field call targetState
which is a specific state key to let the store knows which state I want to update and where to find the exact reducer and just call it, so that time complexity now is O(1).
this is my code, it just for experiment, so it might be not good enough.
dispatch({type: 'ADD_TODO', targetState: 'todos'})
let hasChanged = false
let nextState: StateFromReducersMapObject<typeof reducers> = {}
if (action.targetState) {
nextState = { ...state }
const reducer = finalReducers[action.targetState]
const prevTargetState = state[action.targetState]
const nextTargetState = reducer(prevTargetState, action)
if (typeof nextTargetState === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(
action.targetState,
action
)
throw new Error(errorMessage)
}
nextState[action.targetState] = nextTargetState
hasChanged = hasChanged || nextTargetState !== prevTargetState
} else {
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
hasChanged =
hasChanged || finalReducerKeys.length !== Object.keys(state).length
}
return hasChanged ? nextState : state
Thank you for reading
Issue Analytics
- State:
- Created 3 years ago
- Comments:5 (1 by maintainers)
Top GitHub Comments
This has been covered in the FAQ since 2016:
https://redux.js.org/faq/performance#wont-calling-all-my-reducers-for-each-action-be-slow
donotknow