Batching
You may want to modify multiple observables at once without triggering callbacks for each change. Batching postpones renders and listeners until the end of the batch.
Batching can be done in two ways, wrapping between beginBatch()
and endBatch()
or in a callback with batch(callback)
.
import { batch, beginBatch, endBatch } from "@legendapp/state"
// Wrap in begin and end
beginBatch()
doManyChanges()
endBatch()
// Or batch with a callback
batch(() => {
doManyChanges()
})
When to batch
As we all know, you generally shouldn't optimize pre-emptively. observable
already batches changes under the hood when making modifications, so listeners don't get called until the full change is complete.
Batching is important in a few key situations:
Observables depend on each other
Use batch
to delay computations/renders until all dependent changes are complete.
const name = observable({ first: '', last: '' })
const fullName = computed(() => `${name.first} ${name.last}`)
observe(() => console.log('fullName = ', fullName.get()))
// ❌ fullName computes with incomplete state
// fullName = "First "
name.first.set('First')
// ✅ fullName computes with final state
// fullName = "First Last"
batch(() => {
name.first.set('First')
name.last.set('Last')
})
Prevent excessive renders
Making multiple changes in a row can cause the React hook to re-render multiple times when it should wait until changes are complete.
const name = observable({ items: [] })
function addItems() {
for (let i = 0; i < 1000; i ++) {
obs.items.push({ text: `Item ${i}` })
}
}
// ❌ This can render 1000 times while pushing to the array
addItems()
// ✅ Batching delays until complete and renders once
batch(addItems)
When persisting
If you are using persistObservable
to automatically persist your changes, you can prevent excessive writes by delaying persistence until changes are complete. Pushing to an array 1000 times could save to storage 1000 times, which could be bad!