Replies: 3 comments 2 replies
-
|
Measure synchronously at the moment you trigger the change, then The reason it stopped working is the effect ordering change in async mode. From the await expressions docs: "block effects like The pattern that does work: capture the "before" state at the source of the mutation (an event handler, or right before you assign to For your autoscroll example, you don't actually need <script>
import { settled } from 'svelte';
let viewport;
let messages = $state([]);
async function add(msg) {
// measure BEFORE the state change
const autoscroll =
viewport &&
viewport.offsetHeight + viewport.scrollTop > viewport.scrollHeight - 50;
messages = [...messages, msg];
// wait for the {#each} to render the new node + any async work
await settled();
if (autoscroll) viewport.scrollTo(0, viewport.scrollHeight);
}
</script>
<div bind:this={viewport}>
{#each messages as message (message.id)}
<div>{message.text}</div>
{/each}
</div>For a FLIP-style "measure old position, measure new position, invert" case where the mutation can come from anywhere (not a single handler), the same shape applies: snapshot the rects of the elements you care about, mutate state, If you genuinely have no single mutation site and must react to arbitrary state, you can still read the current DOM in a plain There's an open issue tracking the gap with React's |
Beta Was this translation helpful? Give feedback.
-
|
In Svelte 5, synchronous reactivity means state updates apply immediately. Svelte schedules a DOM update microtask to batch visual changes, making the old To measure the DOM before a state change applies (e.g. for maintaining scroll position in chat windows), you can perform your measurement synchronously right before mutating the state, then await the DOM update using Here is the idiomatic Svelte 5 implementation: <script>
import { tick } from 'svelte';
let messages = $state([]);
let chatWindow;
async function handleAddMessage(newMessage) {
// 1. Measure the DOM synchronously BEFORE the state changes
const isAtBottom =
chatWindow.scrollHeight - chatWindow.scrollTop <= chatWindow.clientHeight + 10;
// 2. Mutate the state
messages.push(newMessage);
// 3. Wait for Svelte 5 to update the DOM
await tick();
// 4. Act on the pre-update measurement
if (isAtBottom) {
chatWindow.scrollTo({
top: chatWindow.scrollHeight,
behavior: 'smooth'
});
}
}
</script>
<div bind:this={chatWindow} class="chat-box">
{#each messages as msg}
<p>{msg}</p>
{/each}
</div> |
Beta Was this translation helpful? Give feedback.
-
|
in async mode, the svelte 5 equivalent is to capture the measurement synchronously in the event handler before you mutate state: function onAddItem() {
const prev = list.scrollHeight; // measure before state change
items.push(newItem);
await tick(); // wait for dom update
list.scrollTop += list.scrollHeight - prev; // apply delta
}this works because the measurement happens before svelte reacts to the state mutation. for a general autoscroll that does not have a specific trigger, you can use a resizeobserver on the container instead, which fires after every dom change without needing access to the previous snapshot: new ResizeObserver(() => {
el.scrollTop = el.scrollHeight;
}).observe(el);this is more robust than |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi, wondering if there will be some mechanism to measure the dom before state change given
$effect.preis now unreliable in that regard when async mode is enabled. I figured there are still valid use cases like this autoscroll example here. I suppose you can model this same behavior with event listeners +$statein this particular case but I'm not sure if that is something that is true for all use cases.Wondering if its recommended anymore to measure the DOM before a state change. If not, will we see
$effect.preget deprecated and a generic alternative documented?Beta Was this translation helpful? Give feedback.
All reactions