Memos (Computed Values)
Memos are derived values that cache their result and only recompute when dependencies change.
Creating Memos
import { createSignal, createMemo } from '@luna_ui/luna';
const [count, setCount] = createSignal(2);
// Computed value that depends on count
const doubled = createMemo(() => count() * 2);
console.log(doubled()); // 4
setCount(5);
console.log(doubled()); // 10
Why Use Memos?
1. Avoid Redundant Computation
Without memo, expensive calculations run every time:
// Bad: filterItems runs on every render
function ItemList() {
const [items, setItems] = createSignal([...]);
const [filter, setFilter] = createSignal("");
return (
<ul>
{items().filter(i => i.name.includes(filter())).map(item => (
<li>{item.name}</li>
))}
</ul>
);
}
// Good: filterItems only runs when items or filter change
function ItemList() {
const [items, setItems] = createSignal([...]);
const [filter, setFilter] = createSignal("");
const filteredItems = createMemo(() =>
items().filter(i => i.name.includes(filter()))
);
return (
<ul>
{filteredItems().map(item => (
<li>{item.name}</li>
))}
</ul>
);
}
2. Share Computed Values
const [firstName, setFirstName] = createSignal("John");
const [lastName, setLastName] = createSignal("Doe");
// Shared computed value
const fullName = createMemo(() => `${firstName()} ${lastName()}`);
// Used in multiple places - computed once
<h1>Welcome, {fullName()}</h1>
<p>Logged in as {fullName()}</p>
Memos Are Cached
Memos only recompute when their dependencies change:
const [count, setCount] = createSignal(0);
const [other, setOther] = createSignal(0);
const expensive = createMemo(() => {
console.log("Computing...");
return count() * 2;
});
expensive(); // Logs "Computing...", returns 0
expensive(); // No log (cached), returns 0
setOther(5); // Does NOT trigger recomputation
expensive(); // No log (cached), returns 0
setCount(1); // Triggers recomputation
expensive(); // Logs "Computing...", returns 2
Chained Memos
Memos can depend on other memos:
const [price, setPrice] = createSignal(100);
const [quantity, setQuantity] = createSignal(2);
const [taxRate, setTaxRate] = createSignal(0.1);
const subtotal = createMemo(() => price() * quantity());
const tax = createMemo(() => subtotal() * taxRate());
const total = createMemo(() => subtotal() + tax());
console.log(total()); // 220
setQuantity(3);
console.log(total()); // 330 (only affected memos recompute)
Memo vs Effect
| Aspect | Memo | Effect |
|---|---|---|
| Returns value | Yes | No |
| Caches result | Yes | No |
| For | Derived data | Side effects |
| Lazy | Yes (computed on read) | No (runs immediately) |
// Use Memo for computed values
const doubled = createMemo(() => count() * 2);
// Use Effect for side effects
createEffect(() => {
document.title = `Count: ${count()}`;
});
Common Patterns
Filtered/Sorted Lists
const [items, setItems] = createSignal([...]);
const [sortBy, setSortBy] = createSignal("name");
const [filterText, setFilterText] = createSignal("");
const displayItems = createMemo(() => {
return items()
.filter(i => i.name.includes(filterText()))
.sort((a, b) => a[sortBy()].localeCompare(b[sortBy()]));
});
Validation
const [email, setEmail] = createSignal("");
const [password, setPassword] = createSignal("");
const isValid = createMemo(() => {
const emailOk = email().includes("@");
const passwordOk = password().length >= 8;
return emailOk && passwordOk;
});
<button disabled={!isValid()}>Submit</button>
Conditional Logic
const [user, setUser] = createSignal(null);
const greeting = createMemo(() => {
const u = user();
if (!u) return "Please log in";
if (u.isAdmin) return `Welcome, Admin ${u.name}`;
return `Hello, ${u.name}`;
});
Try It
Create a shopping cart with:
An
itemssignal (array of{name, price, quantity})A
subtotalmemoA
taxmemo (10%)A
totalmemo
Solution
const [items, setItems] = createSignal([
{ name: "Apple", price: 1.00, quantity: 3 },
{ name: "Banana", price: 0.50, quantity: 5 },
]);
const subtotal = createMemo(() =>
items().reduce((sum, item) => sum + item.price * item.quantity, 0)
);
const tax = createMemo(() => subtotal() * 0.1);
const total = createMemo(() => subtotal() + tax());
// Display
<p>Subtotal: ${subtotal().toFixed(2)}</p>
<p>Tax: ${tax().toFixed(2)}</p>
<p>Total: ${total().toFixed(2)}</p>
Next
Learn about Batch Updates โ