EntityAdapter
The Entity Adapter in the example serves as a boilerplate-killer and state normalizer for your collection of Item
entities. Its main purposes are:
-
Normalize your state shape
Instead of hand-rollingthe adapter gives you that structure automatically (with its
ids
array andentities
lookup map), plus whatever extra flags you pass in (loading
,error
). -
Immutable update helpers
—addOne
,addMany
,updateOne
,removeOne
,removeAll
, etc.
Each method takes your current state and returns a brand-new state with exactly the right pieces changed (and never mutates the old state). In the example:-
On createItemSuccess,
itemAdapter.addOne(item, state)
inserts the new item into bothids
andentities
in one call. -
On clearItems,
itemAdapter.removeAll(state)
wipes out every item but lets you preserve or explicitly reset your extra flags (loading
,error
).
-
-
Selectors out of the box
You don’t have to write boilerplate selectors to readallItems
,entitiesById
, ortotalCount
. A single call toitemAdapter.getSelectors()
gives you typed, memoized selectors likeselectAll
andselectEntities
. -
Consistency & performance
By centralizing all add/update/remove logic in one tested library, you avoid subtle bugs (forgot to update one part of the state) and get optimized updates (e.g. quick lookups via the map rather than array scans).
Declares an Effect
-
createEffect(() => …)
tells NgRx “here is a stream of work I want you to run whenever actions flow through.” -
The returned observable (the inner
this.actions$.pipe(…)
) is subscribed by the Effects system.
Listens to the Actions stream
-
this.actions$
is an injected stream of every action dispatched in your app. -
You pipe it into RxJS operators to filter and transform.
Filters for the createItem
action
-
ofType(ItemsActions.createItem)
lets onlycreateItem
actions through. -
It also types the payload so that downstream you can destructure
{ item }
.
Performs an asynchronous side-effect
-
mergeMap(({ item }) => this.itemService.create(item).pipe(…))
-
For each incoming
createItem
action, it calls your HTTP service methoditemService.create(item)
, which returns anObservable<Item>
. -
mergeMap
ensures that multiple simultaneous create requests can all run in parallel (as opposed toswitchMap
, which would cancel previous requests).
-
Maps the HTTP result back into a new action
-
On success,
.pipe(map(created => ItemsActions.createItemSuccess({ item: created })))
-
Wraps the newly created item in a
createItemSuccess
action, which will get dispatched automatically by NgRx Effects.
-
Handles errors by dispatching a failure action
-
.pipe(catchError(err => of(ItemsActions.createItemFailure({ error: err.message }))))
-
Catches any HTTP or network error, wraps it in a
createItemFailure
action, and emits that instead.
-
1. Model & Adapter
2. Actions
3. Reducer
4. Selectors
5. Effects (with cache-check)
6. HTTP Service
7. Module Registration
8. Component Usage
Flow summary
-
Dispatch
getItem({ id })
. -
Effect checks
selectItemEntities
once:-
If found, it emits
getItemSuccess({ item: cached })
→ reducer upserts (no-op change). -
If not, it calls
/api/items/:id
→ on success emitsgetItemSuccess({ item })
→ reducer upserts new item.
-
-
Reducer on success runs
itemAdapter.upsertOne(...)
, merging into the normalized store.
This ensures you only hit the API when you truly need to, and always keep your cache in sync via the Entity Adapter.
No comments:
Post a Comment