'Svelte: how to wait for component render
I'm making a simple app based on socket connection. Basically one user is deciding what other users can see. One of functionalities is to start timer for other users. So, there is a Timer component that has timerState variable and startTimer function. Timer component is rendered alond with whole user view when user connects to socket. And reacting to incoming event "timer was started" is easy and works perfectly. Problem starts when timer is already started and user refreshes his browser (or just connects late, when timer was already set). Information that timer was set is stored in data object, which is sent to user as welcome event after initial connection to socket.
To simplify, users view is composed like this:
{#if !isDataComplete}
<WaitingComponent>
{:else}
<Timer bind:{tools}>
{/if}
Tools is an object that includes some methods, mainly startTimer method.
So, after connection, user receives data object, which switches wiev from waiting to app wiev (here: timer, initially empty, but rendered). If there is a timer property, that means a timer was already set and startTimer method should be invoked. But, startTimer method is not accessible yet, because script was faster than render and Timer component is not already there. data is not fetched actually, it comes with "welcome" eventfrom socket, so awaiting data does not do the trick, I need to await for component to render.
let isDataComplete = false;
socket.on('welcome', async (incomingData) => {
isDataComplete = incomingData.isAppReady; // triggers switch view if true
await timeout(500); // my "lame solution" (timeout is just setTimeout wrapped in Promise to make that awaitable)
if (data.timer) {
const currTime = Date.now();
if (currTime < data.timer.start + data.timer.duration) {
const remainingTime = Math.floor(
(data.timer.start + data.timer.duration - currTime) /
1000
);
tools.startTimer(remainingTime, true);
}
}
});
As you can see: I "solved" that by adding await setTimeout for 500ms in script (before calling startTimer)- that does the trick, component managed to render and method is already accesible. But somehow I feel that is the lame solution, so maybe somebody could help with some other way?
I simply could put timer component outside the if block, to make it render even before receiving data, but that's not really good idea because of view structure.
I was trying to store setTimer method somewhere else and import it, but it causes some difficulties with managing state of timer from outside. But it is of course a valid way to try.
Thanks a lot! Przemek
Solution 1:[1]
I believe what you want is the tick
function.
Returns a promise that resolves once any pending state changes have been applied, or in the next microtask if there are none.
https://svelte.dev/docs#run-time-svelte-tick
With it you can insert await tick();
where you want your code to wait for all stage changes to be applied and the template updated.
Solution 2:[2]
You can use:
{#await yourFunctionYouawait}
<p>wait...</p>
{:then yourResult}
<p>
{ yourResult }
</p>
{/await}
here you will find more explications.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Karl-Johan Sjögren |
Solution 2 | Deotyma |