0

The following simplified example works but not sure it's the most efficient approach because I'm calling getLocation() multiple times within a Svelte each block per iteration:

<script>
  export let schedule = {
    locations: [
      {
        _id: 1,
        name: 'Boston',
        parking: 'Onstreet'
      },
      {
        _id: 2,
        name: 'New York',
        parking: 'Paid lot'
      }
    ],
    sections: [
      {
        locationId: 2,
        day: 1
      },
      {
        locationId: 1,
        day: 2
      }
    ]
  }

  function getLocation(id) {
    return schedule.locations.find( ({ _id }) => _id === id )
  }
</script>

<template>
  {#each schedule.sections as section}
    <p>{getLocation(section.locationId).name} {getLocation(section.locationId).parking}</p>
  {/each}
</template>

Other approaches I could use:

  1. Populate a Map object with the locationId as the key and location object as the value and reference it in the each block. Was thinking it probably has more overhead since I have to build the map and then lookup the value by key each time anyway.
  2. Denormalize the data by adding location properties to the section objects before displaying. As I'm passing in the data, did not want to mutate it or have the overhead of cloning.
  3. This one is easy on the eyes but perhaps less efficient because of the iteration... create a one-element array with location object returned from getLocation() and use an inner each block to access the location properties like this...
<template>
  {#each schedule.sections as section}
    {#each [getLocation(section.locationId)] as location}
      <p>{location.name} {location.parking}</p>
    {/each}
  {/each}
</template>

I'm new to Svelte. Any thoughts on what the most efficient approach would be? Was my first approach the best (or am I way off base)?

1 Answer 1

1

I'd say the answer just depends on what's going on in with the data.

I usually try to avoid using any functions as part of the template (excluding actions). This default practice comes from using Vue2, where the use of computed can be easily used to inject cached data instead of computing in-place on re-render. In svelte though, I've found that the reactivity does a pretty good job of determining which parts to update within a component.

here's an example (copy paste to repl)

<script>
    const list = ['uno', 'dos', 'tres',  'catorce'];
    let somethingRandom = true
    const splitWord = (word) => {
        console.log(`split [${word}]`)
        return word.split('')
    };
</script>

{#each list as word}
<div>
    {#each splitWord(word) as letter}
        <span>{letter}</span>
        <input type="checkbox" bind:checked={somethingRandom}> <!-- causes re-render-->
    {/each}
</div>  
<input bind:value={word}> <!-- causes re-render -->
<input type="checkbox" bind:checked={somethingRandom}><!-- does not cause to re-render -->
{/each}
  • As long as you have the <input type="checkbox" bind:checked={somethingRandom}> within the inner loop({#each splitWord(word) as letter}), the internal loop is re-rendered.

  • If you comment the inner somethingRandom input out and only have it in the outer loop, changing somethingRandom value will not cause the inner-loop to re-render.

  • Changing <input bind:value={word}> however will cause the internals to re-render

  • The entire list is always re-rendered

So if you have a huge dataset and schedule.sections and/or schedule.locations are being edited, then you might want to consider adding some form of caching. Svelte watches an entire array or object for changes, so if you want to implement something that can cache based on individual item change, you may need to implement a lot of the caching mechanism manually.

If the data is rendered on time, an it doesn't change, there is little benefit (in terms of performance anyway) to cache or de-normalize.

Not the answer you're looking for? Browse other questions tagged or ask your own question.