myHotTake

NgRx Selectors: How Memoization Saves Performance

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.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *