Quantcast
Channel: Active questions tagged svelte - Stack Overflow
Viewing all articles
Browse latest Browse all 1541

Why does Svelte update the middle component after the parent and child?

$
0
0

See https://svelte.dev/repl/89b164da451a45a48ad12ca8f113b13a?version=4.2.15. This a condensed example of what's going on in my application.

  • There are 3 components: App, which renders Layout, which renders Navigation.
  • There are two stores: url and username.
  • App has a reactive statement $: if (!$username && $url !== '/login') { url.set('/login') } (e.g. to redirect back to the login page if not logged in).
  • Navigation is conditionally rendered only if the URL is not /login.
  • Navigation throws an error if username is falsey.
  • Pressing the logout button does username.set(null).

stores.js:

import { writable } from 'svelte/store';export const username = writable();export const url = writable('/login');

App.svelte:

<script>    import { url, username } from './stores';    import Layout from './Layout.svelte';    $: {        console.log('App update', $url, $username);        if (!$username && $url !== '/login') {            console.info('Redirect to login');            url.set('/login');        }    }</script>{console.log('App render', $url, $username), ''}<Layout />

Layout.svelte:

<script>    import { url, username } from './stores';    import Navigation from './Navigation.svelte';    // Adding $username here seems to fix it?    $: console.warn('Layout update', $url);</script>{(console.warn('Layout render', $url), '')}{#if $url !== '/login'}<Navigation /><button on:click={() => {                username.set(null);        }}>LOGOUT</button>{:else}<button on:click={() => {                username.set('admin');                url.set('/');        }}>LOGIN</button>{/if}

Navigation.svelte:

<script>    import { url, username } from './stores';    $: try {        console.log('Nav update', $url, $username);        if (!$username) {            throw new Error('Invalid user');        }    } catch (err) {        console.error('EEE', err);        //throw err;    }</script>{console.log('Nav render', $url), ''}

What should happen:

  1. Pressing the logout button sets username to null.
  2. App reacts to username changing and sets url to /login.
  3. Layout reacts to url changing and unmounts Navigation.
  4. Navigation doesn't throw an error.

What actually happens:

  1. Pressing the logout button sets username to null.
  2. App reacts to username changing and sets url to /login.
  3. Navigation reacts to username changing and throws an error.
  4. (if we catch the error and allow svelte to continue) Layout reacts to url changing and unmounts Navigation.

What I would like to understand is why this is happening. Why does svelte update the parent and the child before updating the middle component?

  • Passing the url down to Layout as prop doesn't seem to fix it which I find even more surprising.
  • Adding $username to Layout fixes it.

How can you safely use conditional rendering if it's not guaranteed that a component will be unmounted before it gets updated with potentially invalid state? What am I missing here?


Viewing all articles
Browse latest Browse all 1541

Trending Articles