
Existing member? Log in and continue learning

See if you like it, start the course for free!

Unlock full course by purchasing a membership
Refactoring a Complex State Service
Refactoring a Complex State Service
I mentioned at the end of the last lesson that the first example we looked at
for implementing connect
was a bit of an ideal case that ends up looking
super clean. It is still realistic, and often our state will end up looking
like this (in fact, the example we looked at was a snippet from the next
application we will be building).
However, things are complicated a little when we also need to access the previous state in order to set our new state. Our Quicklists application does that a lot, so to see how this works we will refactor the services in the Quicklists application.
Again, if you do not already have the completed Quicklists application on your computer you can find it here.
ng add ngxtension
Refactoring the Checklist Service
Let’s start by refactoring the ChecklistService
. The only thing we typically
need to touch when doing these refactors to use connect
is our reducers
— our sources and everything else can remain the same.
This is what the reducers look like for our ChecklistService
without
connect
:
constructor() { // reducers this.checklistsLoaded$.pipe(takeUntilDestroyed()).subscribe({ next: (checklists) => this.state.update((state) => ({ ...state, checklists, loaded: true, })), error: (err) => this.state.update((state) => ({ ...state, error: err })), });
this.add$.pipe(takeUntilDestroyed()).subscribe((checklist) => this.state.update((state) => ({ ...state, checklists: [...state.checklists, this.addIdToChecklist(checklist)], })) );
this.remove$.pipe(takeUntilDestroyed()).subscribe((id) => this.state.update((state) => ({ ...state, checklists: state.checklists.filter((checklist) => checklist.id !== id), })) );
this.edit$.pipe(takeUntilDestroyed()).subscribe((update) => this.state.update((state) => ({ ...state, checklists: state.checklists.map((checklist) => checklist.id === update.id ? { ...checklist, title: update.data.title } : checklist ), })) );
// effects effect(() => { if (this.loaded()) { this.storageService.saveChecklists(this.checklists()); } }); }
We will be refactoring all of this, but the bit specifically that we are focusing on is stuff like this:
this.add$.pipe(takeUntilDestroyed()).subscribe((checklist) => this.state.update((state) => ({ ...state, checklists: [...state.checklists, this.addIdToChecklist(checklist)], })) );
This source emits the new checklist
to add. However, in order to set the new
checklists
state we first need to access all of the existing checklists in the
previous state with ...state.checklists
and then add our new checklist to
those.
This is the way we used connect
in the last lesson:
constructor() { // reducers const nextState$ = merge( this.userAuthenticated$.pipe(map(() => ({ status: 'success' as const }))), this.login$.pipe(map(() => ({ status: 'authenticating' as const }))), this.error$.pipe(map(() => ({ status: 'error' as const }))) );
connect(this.state).with(nextState$); }
There is no previous state here — we just map our sources to whatever we want the next state to be.
However, and again you may remember this from the lesson where we broke down
exactly how connect
works, we can also optionally supply a reducer
function to connect
.
We will update the entire ChecklistService
in just a moment, but dealing with
the add$
source in isolation would look like this:
connect(this.state) .with(this.add$, (state, checklist) => ({ checklists: [...state.checklists, this.addIdToChecklist(checklist)], }))
Instead of just supplying the source
, we also supply a reducer function.
This reducer function will be given the previous state (
Thanks for checking out the preview of this lesson!
You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).