2

I have a svelte component that can be toggled on or off and that fetches some async data. This async call is slow so I want to avoid making it, and only want to make the call when the component is visible (toggled on).

I want to know if there is a way to render this component once, then re-render only if the async call changes and not when toggling on or off? I almost want to "cache" the component.

I have tried two approaches that both work but have downsides.

See this REPL for full working example of both approaches: https://svelte.dev/repl/397fdf11988c4d4fbb98d375adfc8539?version=3

Both of these approaches use this component to display the result:

<!-- DisplayResult.svelte -->

<script lang="ts">
    export let getResultFn; 
    $: colorPromise = getResultFn();
</script>

{#await colorPromise}
    Loading...
{:then color}
    The color is {color}
{/await}

Approach 1: Render component then hide

The first is to always render the component and then hide it when not active. The downside to this is that when the async call changes, it is immediately called which is expensive. I would rather only make this call initially when the component is visible (the toggle is used many times so I only want to get new data for open toggles). Furthermore, I cant use transitions here since the component is never re-rendered

<!-- Item.svelte -->
<button on:click={() => { active = !active;}} >
    Toggle
</button>

        
<div 
    transition:slide
    class:hidden={!active}
>           
    <DisplayResult getResultFn={() => getResults(idx)} />
</div>

<style>
.hidden {
     display: none;
}
</style>

Approach 2: Conditionally toggle

The other option is to create the component when visible, then destory when hidden. The good part is this only gets the data when the component is visible but then it makes extra calls every time the component is subsequently toggled. I can also use transitions here which is nice

<!-- Item.svelte -->
<button on:click={() => { active = !active;}} >
    Toggle
</button>

        
{#if active}
 <div transition:slide >
    <DisplayResult getResultFn={() => getResults(idx)} />
 </div>
{/if}

Best of both?

Is there a way to get the best of both worlds and

  1. Only get data initially when component is actually visible so can lazily fetch (like Approach 2)
  2. But then cache call and do not re-render every time (like Approach 1)

1 Answer 1

1

re-render only if the async call changes

In your example colors are refetched every time idx changes. Is this neccessary because the color values might change in the db in the meantime?

Assuming that not and that it's ok to load the data once, this could be moved to a seperate file. So the data will persist when the component isn't rendered

REPL

colors.js
export let colors = null

export async function initColors() {
    if(!colors) {
        await new Promise(f => setTimeout(f, 2000));
        colors = ['red', 'yellow', 'blue'];
    }
}
DisplayResult.svelte
<script>
    import {colors, initColors} from './colors'

    export let idx
</script>

{#await initColors()}
    Loading...
{:then _}
    The color is {colors[idx]}
{/await}
Item.svelte
<script>
    import DisplayResult from './DisplayResult.svelte'
    import {slide} from "svelte/transition"
    
    export let idx
    let active
</script>

<div class="item">
    <button on:click={() => { active = !active;}} >
        {active ? 'Close' : 'Open'}
    </button>

    {#if active}
    <div transition:slide >
        <DisplayResult {idx} />
    </div>
    {/if}
</div>

<style>
    .item {
        border: 1px solid black;
        padding: 5px;
        margin: 5px
    }
</style>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.