Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

createBatch reference doc rewrite #982

Merged
merged 7 commits into from
Dec 18, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 85 additions & 4 deletions src/routes/reference/reactive-utilities/batch.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,88 @@ import { batch } from "solid-js"
function batch<T>(fn: () => T): T
```

This is a low level API that is used by Solid to batch updates.
It holds executing downstream computations within the block until the end to prevent unnecessary recalculation.
Solid Store's set method, Mutable Store's array methods, and Effects automatically wrap their code in a batch.
This is useful for when you want to batch a set of computations that are not wrapped in a batch already.
`batch` is a low-level API that batches updates together.
More precisely, `batch(fn)` holds the execution of downstream computations
during the `fn` block, executing them all together once the block `fn` returns.
Thus, instead of a downstream computation executing after every dependency
update, it will update just once at the end of the batch.

Batching improves performance by avoiding unnecessary recalculation.
Suppose you have a downstream memo `down` that depends on
multiple upstream signals `up1`, `up2`, and `up3`:

```ts
import { createSignal, createMemo, createEffect } from "solid-js"
const [up1, setUp1] = createSignal(1)
const [up2, setUp2] = createSignal(2)
const [up3, setUp3] = createSignal(3)
const down = createMemo(() => up1() + up2() + up3())
// For illustration, monitor when `down` gets recomputed:
createEffect(() => console.log(down())) // outputs 6
```

If you directly update all of the upstream signals outside of batch mode,
then `down` will recompute every time.

```ts
setUp1(4) // recomputes down, outputs 9
setUp2(5) // recomputes down, outputs 12
setUp3(6) // recomputes down, outputs 15
```

If instead you update the upstream signals within a `batch`, then `down`
will update only once at the end:

```ts
batch(() => {
setUp1(10) // doesn't update down yet
setUp2(10) // doesn't update down yet
setUp3(10) // doesn't update down yet
}) // recomputes down, outputs 30
```

The impact is even more dramatic if you have *m* downstream computations
(memos, effects, etc.) that each depend on *n* upstream signals.
Without batching, modifying all *n* upstream signals
would cause *m n* updates to the downstream computations.
With batching, modifying all *n* upstream signals
would cause *m* updates to the downstream computations.
Given that each update takes at least *n* time
(just to read the upstream signals), this cost savings can be significant.
Batching is also especially helpful when the downstream effects include
DOM updates, which can be expensive.

Solid uses `batch` internally to automatically batch updates for you
in a few cases:

* Within [effects](/reference/basic-reactivity/effects)
and [`onMount`](/reference/lifecycle/on-mount)
* Within the [setter of a store](/reference/store-utilities/create-store#setter)
(which can update several properties at once)
* Within array methods (e.g. `Array.prototype.splice`) of a
[mutable store](/reference/store-utilities/create-mutable-store)
(which can update several elements at once)

These save you from having to use `batch` yourself in many cases.
For the most part, automatic batching should be transparent to you,
because accessing a memo will cause it to recompute if it is out of date.
For example:

```ts
batch(() => {
setUp1(11) // doesn't update down yet
setUp2(11) // doesn't update down yet
setUp3(11) // doesn't update down yet
console.log(down()) // recomputes down, outputs 33
setUp1(12) // doesn't update down yet
setUp2(12) // doesn't update down yet
setUp3(12) // doesn't update down yet
}) // recomputes down, outputs 36
```

You can think of `batch(fn)` as setting a global "batch mode" variable,
calling the function `fn`, and then restoring the global variable to its
previous value.
This means that you can nest `batch` calls, and they will form one big batch.
It also means that, if `fn` is asynchronous,
only the updates before the first `await` will be batched.
Loading