Skip to content

Commit 957cffa

Browse files
Improve createMemo reference page (#1355)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent df0a9ec commit 957cffa

File tree

1 file changed

+179
-48
lines changed

1 file changed

+179
-48
lines changed

src/routes/reference/basic-reactivity/create-memo.mdx

Lines changed: 179 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,77 +10,208 @@ tags:
1010
- derived-state
1111
- caching
1212
- reactivity
13-
version: '1.0'
13+
version: "1.0"
1414
description: >-
1515
Use createMemo to efficiently compute and cache derived values. Prevent
1616
expensive recalculations and optimize your Solid.js application's performance.
1717
---
1818

19-
Memos let you efficiently use a derived value in many reactive computations.
20-
`createMemo` creates a readonly reactive value equal to the return value of the given function and makes sure that function only gets executed when its dependencies change.
19+
The `createMemo` function creates a read-only signal that derives its value from other reactive values.
20+
The calculated value is memoized: the calculation runs only when dependencies change, and is reused when the value is read.
21+
When a dependency changes, the calculation re-executes.
22+
If the new result is equal to the previous result (according to the [`equals`](#equals) option), the memo suppresses downstream updates.
2123

22-
```tsx
23-
import { createMemo } from "solid-js"
24+
## Import
25+
26+
```ts
27+
import { createMemo } from "solid-js";
28+
```
29+
30+
## Type
2431

32+
```ts
2533
function createMemo<T>(
2634
fn: (v: T) => T,
2735
value?: T,
28-
options?: { equals?: false | ((prev: T, next: T) => boolean) }
29-
): () => T
30-
36+
options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string }
37+
): () => T;
3138
```
3239

33-
Here's an example of how createMemo can be used:
40+
## Parameters
3441

35-
```ts
36-
const value = createMemo(() => computeExpensiveValue(a(), b()))
42+
### `fn`
3743

38-
//read the value
39-
value()
40-
```
44+
- **Type:** `(v: T) => T`
45+
- **Required:** Yes
4146

42-
In Solid, you often don't need to wrap functions in memos; you can alternatively just define and call a regular function to get similar reactive behavior.
43-
The main difference is when you call the function in multiple reactive settings.
44-
In this case, when the function's dependencies update, the function will get called multiple times unless it is wrapped in createMemo.
45-
For example:
47+
The function that calculates the memo's value.
4648

47-
```tsx
48-
const user = createMemo(() => searchForUser(username()))
49-
// compare with: const user = () => searchForUser(username());
50-
return (
51-
<ul>
52-
<li>Your name is {user()?.name}</li>
53-
<li>
54-
Your email is <code>{user()?.email}</code>
55-
</li>
56-
</ul>
57-
)
58-
```
49+
It receives the value returned from the previous execution as its argument.
50+
On the first execution, it receives the `value` parameter (if provided) or `undefined`.
51+
52+
This function should be pure (it should not modify other reactive values).
53+
54+
### `value`
55+
56+
- **Type:** `T`
57+
- **Required:** No
58+
59+
The initial value passed to `fn` on its first execution.
60+
61+
### `options`
62+
63+
- **Type:** `{ equals?: false | ((prev: T, next: T) => boolean); name?: string }`
64+
- **Required:** No
5965

60-
When the username signal updates, searchForUser will get called just once.
61-
If the returned user actually changed, the user memo updates, and then both list items will update automatically.
66+
An optional configuration object with the following properties:
6267

63-
If we had instead defined user as a plain function `() => searchForUser(username())`, then `searchForUser` would have been called twice, once when updating each list item.
68+
#### `equals`
6469

65-
Another key difference is that a memo can shield dependents from updating when the memo's dependencies change but the resulting memo value doesn't.
66-
Like [createSignal](/reference/basic-reactivity/create-signal), the derived signal made by `createMemo` updates (and triggers dependents to rerun) only when the value returned by the memo function actually changes from the previous value, according to JavaScript's `===` operator.
67-
Alternatively, you can pass an options object with `equals` set to false to always update the memo when its dependencies change, or you can pass your own `equals` function for testing equality.
70+
- **Type:** `false | ((prev: T, next: T) => boolean)`
71+
- **Required:** No
6872

69-
The memo function is called with an argument equal to the value returned from the previous execution of the memo function, or, on the first call, equal to the optional second argument to `createMemo`.
70-
This is useful for reducing computations, such as:
73+
A function that runs after each execution of `fn` to determine if the memo value has changed.
74+
It receives the previous and the new value.
75+
If it returns `true`, the values are considered equal and the memo does not trigger downstream updates.
76+
77+
If set to `false` (instead of a function), the memo triggers updates whenever it re-executes, even if the value is unchanged.
78+
79+
Defaults to a function that compares values using strict equality (`===`).
80+
81+
#### `name`
82+
83+
- **Type:** `string`
84+
- **Required:** No
85+
86+
A debug name for the memo.
87+
It is used for identification in debugging tools like the [Solid Debugger](https://github.com/thetarnav/solid-devtools).
88+
89+
## Return value
90+
91+
- **Type:** `() => T`
92+
93+
`createMemo` returns a read-only accessor function.
94+
Calling this function returns the current memoized value.
95+
96+
## Examples
97+
98+
### Basic usage
99+
100+
```tsx
101+
import { createSignal, createMemo, For } from "solid-js";
102+
103+
const NAMES = ["Alice Smith", "Bob Jones", "Charlie Day", "David Lee"];
104+
105+
function FilterList() {
106+
const [query, setQuery] = createSignal("");
107+
108+
// The function executes immediately to calculate the initial value.
109+
// It re-executes only when the `query` signal changes.
110+
const filteredNames = createMemo(() => {
111+
console.log("Calculating list...");
112+
return NAMES.filter((name) => {
113+
return name.toLowerCase().includes(query().toLowerCase());
114+
});
115+
});
116+
117+
return (
118+
<div>
119+
<input
120+
value={query()}
121+
onInput={(e) => setQuery(e.currentTarget.value)}
122+
placeholder="Search..."
123+
/>
124+
125+
{/* Accessing the memo. If dependencies haven't changed, returns cached value. */}
126+
<div>Count: {filteredNames().length}</div>
127+
128+
<ul>
129+
{/* Accessing the memo again does not trigger re-execution. */}
130+
<For each={filteredNames()}>{(name) => <li>{name}</li>}</For>
131+
</ul>
132+
</div>
133+
);
134+
}
135+
```
136+
137+
### Custom equality check
71138

72139
```tsx
73-
// track the sum of all values taken on by input() as it updates
74-
const sum = createMemo((prev) => input() + prev, 0)
140+
import { createSignal, createMemo, createEffect } from "solid-js";
141+
142+
function DateNormalizer() {
143+
const [dateString, setDateString] = createSignal("2024-05-10");
144+
145+
const dateObject = createMemo(
146+
() => {
147+
return new Date(dateString());
148+
},
149+
undefined,
150+
{
151+
// Overrides the default strict equality check (===).
152+
// If this returns true, observers (like the Effect below) are NOT notified.
153+
equals: (prev, next) => {
154+
return prev.getTime() === next.getTime();
155+
},
156+
}
157+
);
158+
159+
createEffect(() => {
160+
// This effect runs only when the numeric time value changes,
161+
// ignoring new Date object references.
162+
console.log("Date changed to:", dateObject().toISOString());
163+
});
164+
165+
return (
166+
<div>
167+
<input
168+
value={dateString()}
169+
onInput={(e) => setDateString(e.currentTarget.value)}
170+
/>
171+
{/* Setting the same date string creates a new Date object,
172+
but `equals` prevents the update propagation. */}
173+
<button onClick={() => setDateString("2024-05-10")}>
174+
Reset to the same date
175+
</button>
176+
</div>
177+
);
178+
}
75179
```
76180

77-
The memo function should not change other signals by calling setters (it should be "pure").
78-
This enables Solid to optimize the execution order of memo updates according to their dependency graph, so that all memos can update at most once in response to a dependency change.
181+
### Accessing previous value
182+
183+
```tsx
184+
import { createSignal, createMemo } from "solid-js";
185+
186+
function TrendTracker() {
187+
const [count, setCount] = createSignal(0);
188+
189+
const trend = createMemo(
190+
// The first argument `prev` is the return value of the previous execution.
191+
(prev) => {
192+
const current = count();
193+
if (current === prev.value) return { value: current, label: "Same" };
194+
return {
195+
value: current,
196+
label: current > prev.value ? "Up" : "Down",
197+
};
198+
},
199+
// The second argument provides the initial value for `prev`.
200+
{ value: 0, label: "Same" }
201+
);
202+
203+
return (
204+
<div>
205+
<div>Current: {trend().value}</div>
206+
<div>Direction: {trend().label}</div>
207+
208+
<button onClick={() => setCount((c) => c + 1)}>Increment</button>
209+
<button onClick={() => setCount((c) => c - 1)}>Decrement</button>
210+
</div>
211+
);
212+
}
213+
```
79214

80-
## Options and arguments
215+
## Related
81216

82-
| Name | Type | Description |
83-
| :------ | :------------------------------------------------------ | :------------------------------------------------------------- |
84-
| fn | `(v: T) => T` | The function to memoize. |
85-
| value | `T` | The initial value of the memo. |
86-
| options | `{ equals?: false \| ((prev: T, next: T) => boolean) }` | An optional object with an `equals` function to test equality. |
217+
- [`createComputed`](/reference/secondary-primitives/create-computed)

0 commit comments

Comments
 (0)