Batch Updates
Batch multiple signal updates to trigger effects only once.
The Problem
Without batching, each signal update triggers effects immediately:
const [firstName, setFirstName] = createSignal("John");
const [lastName, setLastName] = createSignal("Doe");
createEffect(() => {
console.log(`Name: ${firstName()} ${lastName()}`);
});
// Two separate updates = two effect runs
setFirstName("Jane"); // Effect runs: "Jane Doe"
setLastName("Smith"); // Effect runs: "Jane Smith"
This can cause:
Unnecessary re-renders
Inconsistent intermediate states
Performance issues
Using Batch
import { createSignal, createEffect, batch } from '@luna_ui/luna';
const [firstName, setFirstName] = createSignal("John");
const [lastName, setLastName] = createSignal("Doe");
createEffect(() => {
console.log(`Name: ${firstName()} ${lastName()}`);
});
// Batched = one effect run
batch(() => {
setFirstName("Jane");
setLastName("Smith");
});
// Effect runs once: "Jane Smith"
When to Use Batch
Multiple Related Updates
const [user, setUser] = createSignal(null);
const [loading, setLoading] = createSignal(true);
const [error, setError] = createSignal(null);
async function fetchUser() {
setLoading(true);
try {
const data = await api.getUser();
// Update all at once
batch(() => {
setUser(data);
setLoading(false);
setError(null);
});
} catch (e) {
batch(() => {
setUser(null);
setLoading(false);
setError(e.message);
});
}
}
Form Updates
const [form, setForm] = createSignal({
name: "",
email: "",
phone: "",
});
function resetForm() {
batch(() => {
setName("");
setEmail("");
setPhone("");
});
}
State Machines
const [status, setStatus] = createSignal("idle");
const [data, setData] = createSignal(null);
const [progress, setProgress] = createSignal(0);
function startDownload() {
batch(() => {
setStatus("downloading");
setData(null);
setProgress(0);
});
}
Batch is Synchronous
Batch executes synchronously and returns after all updates:
const [count, setCount] = createSignal(0);
batch(() => {
setCount(1);
setCount(2);
setCount(3);
});
console.log(count()); // 3 (immediately after batch)
Nested Batches
Nested batches are flattened - effects run after the outermost batch:
const [a, setA] = createSignal(0);
const [b, setB] = createSignal(0);
createEffect(() => console.log(a(), b()));
batch(() => {
setA(1);
batch(() => {
setB(2); // Still inside outer batch
});
setA(3);
});
// Effect runs once: 3, 2
Batch with Return Value
Batch returns the value from its callback:
const result = batch(() => {
setA(1);
setB(2);
return a() + b();
});
console.log(result); // 3
Common Mistakes
Async Inside Batch
Batch only affects synchronous code:
// Wrong: async breaks batch
batch(async () => {
setLoading(true);
const data = await fetch(...); // Batch ends here!
setData(data); // Not batched
setLoading(false); // Not batched
});
// Correct: batch after async
async function load() {
setLoading(true);
const data = await fetch(...);
batch(() => {
setData(data);
setLoading(false);
});
}
Try It
Create a color mixer with RGB sliders that only updates the preview once when all sliders change:
Solution
const [r, setR] = createSignal(128);
const [g, setG] = createSignal(128);
const [b, setB] = createSignal(128);
const color = createMemo(() => `rgb(${r()}, ${g()}, ${b()})`);
createEffect(() => {
console.log("Color updated:", color());
});
function setPreset(preset) {
batch(() => {
setR(preset.r);
setG(preset.g);
setB(preset.b);
});
}
// Usage
setPreset({ r: 255, g: 0, b: 0 }); // One effect run
Next
Learn about Untrack โ