Background
Selector
is an essential part of the entire NgRx state management system, which is much more complicated than the action
and reducer
part based on my learning and development experience. Sometimes I feel it is like a black box, which hides many excellent designs and techniques. I spend some time and dig into the source code of NgRx to take a look at the internals of the black box. This post (and later posts) will share some interesting points I found during the process.
When using NgRx, developers always do something like this:
1 | export const animalListState: MemoizedSelector<State, Animal[]> = createSelector( |
createSelector
method return a selector function, which can be passed into the store.select()
method to get the desired state out of the store.
By default, the type of the returned function from createSelector
is MemoizedSelector<State, Result>
. Have you ever notice that? This post will introduce what it is and how it works.
What is memoization?
Memoization is a general concept in computer science. Wikipedia explains it as follows:
In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
You can find many articles online for explaining memoization with code examples. Simply speaking, a hash map is used for the cached result. Technically it’s not difficult at all.
Memoization is a great optimization solution of pure function. Generally speaking, A pure function is a function where the return value is only determined by its input values, without side effects
.
As you may know, Selector
is a pure function. memoizedSelector
is just a normal selector function with memoization optimization. Next, let’s see how it works in the design of the NgRx library.
Source code of memoizedSelector
In the source code of NgRx
, you can find the selector
related code in the path of platform/modules/store/src/selector.ts
.
selector.ts
file is roughly 700 lines, which hold all the functionalities of it. There are many interesting points inside this module, which I can share in another article, but this post focus on memoization. So I pick up all the necessary code and put it as follows:
1 | export type AnyFn = (...args: any[]) => any; |
There are many interesting Typescript stuff in the above code block. But for memoization, you can focus on the method defaultMemoize
. In the following section, I will show you how it can make your program run faster.
Explore the memoizedSelector method
To show how memoization works, I create a simple method slowFunction
as following, to simulate that a method running very slowly:
1 | export function slowFunction(val: number): number { |
And then test it with the following scripts:
1 | import { defaultMemoize } from "./memoizedSelector"; |
The output goes as folllowing:
1 | $ ts-node index.ts |
Compared with the original slowFunction
method, the memoized method fastFunction
can directly output the result for the same input. That’s the power of memoization, hope you can master it.