Replies: 4 comments
-
|
@ducin would have to read through it multiple times to fully understand it. Are you sure there is no way you can fix it with an equals function in the computed? That one's quite powerful. Could you add a snippet of how such a parameterized computed would look like? That would help a lot |
Beta Was this translation helpful? Give feedback.
-
|
@rainerhahnekamp @markostanimirovic I have significantly updated the description, and provided an example API to better outline the issue. |
Beta Was this translation helpful? Give feedback.
-
|
Thanks for the clarification Tomasz |
Beta Was this translation helpful? Give feedback.
-
|
Instead of a custom SignalStore feature ( The only problem I see is that it cannot have a const filteredUsers = myComputed(() => {
return users().filter((u) => u.name.includes(query()));
}, { cacheSize: 10 });because we won't be able to resolve what are dependent signals and apply proper caching. Should we reintroduce const filteredUsers = explicitComputed(users, query, (users, query) => {
return users.filter((u) => u.name.includes(query));
}, { cacheSize: 10, ttl: 60 * 1_000 }); // time to leave is 0 by defaultI'm going to convert this issue into a discussion. Feel free to share your thoughts! |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Which @ngrx/* package(s) are relevant/related to the feature request?
signals
Information
TL;DR; Due to
computedcaching only the most recent computation, it could happen, that unnecessarily the calculations could be repeated over and over again. This proposal is about a way to cache parametrized/multiple (dynamically) computed valuesExample Issue
Let's take a look at the following example (from docs):
We can create a computed depending on the current state of the filters:
An (angular) computed value is cached for the current value of all its dependencies (current value of
filter, e.g.query=""andbooks = [...], all works as expected. But when any dependency change, e.g.filter.querychanges, then the entire **visibleBooks` has to be re-computed**. And that could be a heavy operation. And if we keep on switching between two values of filter.query but we have the same books (e.g. fetched once from the API):filter.query==abc// result ->[ {title: "abc 1"}, {title: "abc 2"}, ...]// this result FIRST TIMEfilter.query==xyz// result ->[ {title: "xyz 1"}, {title: "xyz 2"}, ...]// this result FIRST TIMEfilter.query==abc// result ->[ {title: "abc 1"}, {title: "abc 2"}, ...]// this result HAS ALREADY BEEN CALCULATEDfilter.query==xyz// result ->[ {title: "xyz 1"}, {title: "xyz 2"}, ...]// this result HAS ALREADY BEEN CALCULATED...
then each time the
computedgets dirty, has to recompute, even though thebooksis the same and, essentially,books.filterBy('abc')result never changes.This proposal addresses more advanced usecases which, IMO, will appear sooner or later.
The idea is to:
withMemo, withParametrizedComputed` (or whatever the name becomes) could be either a function encapsulating the access to the Map (and calling underlying computeds) or a Proxy (which would essentially do the same.Describe any alternatives/workarounds you're currently using
the only alternative I can think of is:
I would be willing to submit a PR to fix this issue
Example API Proposal
This is just an example of the API, I'm not bound to the API in any way. Design decisions:
bookschange (new response from the API) then all cached results have no meaning anywayfilter, ass long asbooksdon't changebookschange.API:
withParametrizedComputed((state, cacheKeyFn, clearCacheMap) => newComputedsstateis the same as inwithComputed,newComputedsis the same as inwithComputed,cacheKeyFnis a function which returns a cache key, the cache itself is an ES Map (not{}in case the chosen key is an object). Example:({ books, filter }) => filter(yes, books is unused, left it there for readability)clearCacheMap: default (implicit) is{ books: true, filter: true }which means clear ALWAYS whenever eitherbooksorfilterchanges. It gets explicitly overriden by - in our example -{ filter: false }which results in{ books: true, filter: false }(reset only onbookschange).Example:
The underlying ES Map could look like the following:
Map { }- initially empty,visibleBookscomputed never usedMap { "abc" => [bookA, bookB, ...] }- first result of computed,filter=abcMap { "abc" => [bookA, bookB, ...], "xyz" => [bookX, bookY, ...] }- second result of computed,filter=xyzMap { "abc" => [bookA, bookB, ...], "xyz" => [bookX, bookY, ...] }-filter=abcchosen againMap { "abc" => [bookA, bookB, ...], "xyz" => [bookX, bookY, ...] }-filter=xyzchosen againMap { }-bookschanged, cache cleared, but immediately newvisibleBooksvalue gets computed)Map { "abc" => [bookAnotherA, bookAnotherB, ...] }- first result over new books,filter=abcOf course the API is far from perfect, it's just a starting point for the discussion.
Beta Was this translation helpful? Give feedback.
All reactions