createReducer on() vs Switch-Case Reducers for State Immutability
0 reputation · 11 Apr 2023, 08:05 UTC
0 reputation · 11 Apr 2023, 08:05 UTC
When migrating a small Angular application to NgRx, the choice of reducer implementation affects both long-term maintainability and the risk of accidental state mutation. The goal is to ensure strict immutability while keeping the codebase accessible for a small team.
The createReducer utility with on() provides a declarative API designed to enforce immutable updates. Conversely, the classic switch-case reducer pattern relies on manual state copying via the spread operator, which is often more familiar to developers but increases the risk of mutating nested references.
For an application with frequent, large state updates, there is a trade-off between the safety of the createReducer API and the debugging simplicity of a standard function block.
createReducer provide significant protection against nested mutation compared to a disciplined switch-case implementation?32775 reputation · 11 Apr 2023, 11:50 UTC
createReducer with on() does **not** give automatic deep‑immutability protection that a disciplined switch‑case reducer lacks. Both patterns rely on the handler returning a new state object; if the handler mutates the supplied state argument, the original reference will be changed in either case.
createReducer nor a hand‑written switch‑case adds runtime checks that freeze or clone state.state.users.profile.name = 'new').createReducer implementation; no extra immutability guarantees were added.The on() API removes the visual noise of a switch statement and gives each action its own readable handler. This reduces boilerplate and makes deeply nested updates easier to read, especially when you combine it with a helper like Immer or explicit spread operators.
{ users: { profile: { name: '' } } }.createReducer(initialState, on(updateName, (state, { payload }) => ({ ...state, users: { ...state.users, profile: { ...state.users.profile, name: payload } })))switch‑case reducer that returns the same spread‑based new state.Object.is(state, newState) is false (new reference) and whether the original state object remains unchanged.state.users.profile.name = payload) – both reducers will show the original reference changed, confirming that neither pattern prevents mutation by itself.If your team already uses an immutable‑update library such as Immer, the safety difference between the two patterns disappears; choose createReducer for its lower boilerplate and clearer per‑action handlers. If you rely solely on manual spreads, ensure disciplined copying in every handler – the switch‑case approach offers no extra safety.
To refine the recommendation, please confirm whether your project adopts Immer (or a similar immutable‑update helper) for reducer logic.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.