EntityAdapter.upsert vs Custom Reducer for Nested State Merging
0 reputation · 12 Dec 2024, 15:26 UTC
0 reputation · 12 Dec 2024, 15:26 UTC
When managing complex entities with NgRx EntityAdapter, there is a requirement to update specific properties of an entity without losing existing nested collections or optional fields not present in the update payload.
The documented upsertOne and upsertMany methods replace the existing entity by ID. If the dispatched payload contains only a subset of the entity's properties, the remaining fields in the existing state are discarded rather than merged.
This creates a trade-off between using the built-in adapter methods for brevity and implementing a custom reducer to handle deep merging of entity properties to prevent partial state loss.
Should the state merge logic be handled via a custom reducer wrapping the adapter, or is there a documented configuration for EntityAdapter to perform a shallow merge during upsert operations?
34775 reputation · 12 Dec 2024, 16:38 UTC
In NgRx 15+, EntityAdapter.upsertOne and upsertMany replace the entire entity object keyed by ID. Any properties omitted from the dispatched payload—including nested arrays or optional fields—are discarded. The adapter does not expose a configuration option for shallow or deep merging during upsert operations.
The update method can preserve unchanged top-level fields by spreading the existing entity with the payload: state.entities[id] = { ...state.entities[id], ...payload }. This works when only leaf-level properties need updating and nested collections under those leaves are either immutable or replaced entirely.
When nested structures must survive a partial update, a custom reducer that performs a pure deep merge is required. The reducer must return a new state object without mutating the existing entity; mutable operations break NgRx change detection. A typical pattern combines createEntityAdapter with a reducer that merges payload depth-first, preserving arrays and objects not present in the payload.
adapter.update with spread logic.Diagnostic question: Does the update need to preserve nested collections (arrays/objects) under keys not present in the payload, or only top-level property overrides? This determines whether adapter.update suffices or a custom deep-reducer is required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.