Skip to content

Instantly share code, notes, and snippets.

@developit
Last active May 29, 2024 20:30
Show Gist options
  • Save developit/d354d8d55de07e41b4b6e7bfa8fb8017 to your computer and use it in GitHub Desktop.
Save developit/d354d8d55de07e41b4b6e7bfa8fb8017 to your computer and use it in GitHub Desktop.

computedWithMemo()

It's computed(), but with a second argument that gets passed each newly returned value in order to produce a string cache key. If you return the same cache key as the previous run of the computed, no update will be performed (no effects, no renders, etc).

JSFiddle Demo

- computed(
-   () => ['an', 'unstable', 'return', 'value'],
- )
+ computedWithMemo(
+   () => ['an', 'unstable', 'return', 'value'],
+   (value) => value.join(), // a stable return value
+ )

Here's a full example:

// create an array of todo list items that will change often
const todos = signal([]);

// create a computed signal for the array of items with .completed==true
// we only want this computed to update when _completed_ items change
const completed = computedWithMemo(
  // this compute() function returns a new array every time:
  () => events.value.filter(todo => todo.completed),
  // but this getKey() returns a stable string cache key for compute() return value:
  (value) => value.reduce((key, todo) => key + todo.id, ''),
);

// log every time the list of completed todos changes
effect(() => console.log(completed.value));

// This will *not* run the effect, because no completed items were added/removed:
todos.value = [{id:1, completed:false}];

// This *will* run the effect, because the filtered list of completed items produces a new string key:
todos.value = todos.value.concat({id:2, completed:true});
/**
* Key/checksum-based memoized version of `computed()`.
* The provided `getKey()` function returns a cache key for each return value from `compute()`.
* The computed signal will _not update_ for subsequent computed values that result in the same key.
*/
export function computedWithMemo<T = any>(
compute: () => T,
getKey: (obj: T) => any,
) {
let lastKey: T;
let last: T;
return computed<T>(() => {
const result = compute();
const key = getKey(result);
if (last !== undefined && key === lastKey) return last;
lastKey = key;
last = result;
return result;
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment