If this story helps you grasp memoized selectors in NgRx, give it a like or share to spread the clarity! đ
Think of optimizing NgRx with memoized selectors as organizing workout playlists. Iâm at the gym, and of course I want to make sure my workout music is always in sync with my moodâwhether itâs cardio or weightlifting.
Now, Iâve got a huge music library. If I sift through my entire library every single time I change moods, itâs exhausting and slows me down. Thatâs like NgRx without memoized selectors: every time the app state changes, itâs scanning everything to find the right data to display.
But then, I decide to make playlists. One for cardio and another for weights. Hereâs the trick: these playlists donât change unless I explicitly add or remove songs. So, when I need cardio music, I just hit âplay,â and the curated list is instantly there. No wasted time searching. This is exactly what a memoized selector doesâit creates a âplaylistâ of data that only updates when necessary.
And hereâs the genius part: letâs say Iâm listening to my cardio playlist and someone tells me, âHey, thereâs a new song for your run.â If itâs the same song I already have, I donât add it againâI save the effort. Thatâs the memoization kicking in: it ensures the selector doesnât recompute the data if the state hasnât really changed.
Thanks to my playlists (or memoized selectors), I get into the groove faster, my workoutâs smooth, and I donât waste energy on unnecessary work.
So, whenever Iâm optimizing NgRx performance, I remember: curate the data with memoized selectors. Let them do the heavy lifting efficiently, just like my gym playlists.
In the first part, I compared memoized selectors in NgRx to workout playlists, and now letâs see how that analogy translates into JavaScript.
Imagine I have a state that represents a list of workouts and their durations:
const state = {
workouts: [
{ id: 1, type: 'cardio', duration: 30 },
{ id: 2, type: 'strength', duration: 45 },
{ id: 3, type: 'cardio', duration: 20 },
],
};
Without Memoized Selectors
If I want to get all cardio
workouts every time the state changes, I might write something like this:
const selectCardioWorkouts = (state) => {
console.log('Recomputing cardio workouts...');
return state.workouts.filter(workout => workout.type === 'cardio');
};
Every time the state updates, selectCardioWorkouts
recomputes the filtered listâeven if the list hasnât changed! Itâs like going through my entire music library over and over.
With Memoized Selectors
Using NgRxâs createSelector
, I can âcurate the playlistâ and memoize the results:
import { createSelector } from '@ngrx/store';
const selectWorkouts = (state) => state.workouts;
const selectCardioWorkouts = createSelector(
selectWorkouts,
(workouts) => {
console.log('Recomputing cardio workouts...');
return workouts.filter(workout => workout.type === 'cardio');
}
);
Now, the key here is memoization: the selector caches the result of the computation. If the list of workouts hasnât changed, selectCardioWorkouts
will return the cached result without recomputing, even if the state changes elsewhere.
Example in Action
Hereâs how it works when the state changes:
let currentState = { ...state };
console.log(selectCardioWorkouts(currentState)); // Logs "Recomputing cardio workouts..." and returns cardio workouts
console.log(selectCardioWorkouts(currentState)); // Does NOT log "Recomputing" again, uses cached result
currentState = {
...currentState,
workouts: [...currentState.workouts, { id: 4, type: 'cardio', duration: 15 }],
};
console.log(selectCardioWorkouts(currentState)); // Logs "Recomputing cardio workouts..." for the updated state
Key Takeaways
- Efficiency: Memoized selectors only recompute when their input state changes, saving processing power.
- Clarity: By breaking down selectors into smaller pieces, you make your code modular and readable.
- Performance Boost: In complex apps, reducing redundant computations improves the user experience.
Leave a Reply