Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Rerender child component after API returns data

I have a ParentComponent that makes an API call. When the API call is complete, I’d like the ChildComponent that depends on an array of items from that API to rerender with those items.

I’ve seen a trick where you can do something like:

<script>
  if(rerenderComponent) rendered = true;
</script>

{#key rendered}
  <ChildComponent {items} />
{/key}

Where I can force a rerender of that component. This seems like I’m trying to make svelte do something it doesn’t want to do and I wonder if there’s a better more "Svelete"ish way to do this?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Here’s my code:

App.svelte:

<script>
  import ParentComponent from './testing/ParentComponent.svelte'
</script>

<ParentComponent />

ParentComponent.svelte:

<script>
  import { onMount } from 'svelte'
  import ChildComponent from './ChildComponent.svelte'

  let items = []
  let value = ''

  onMount(async function () {
    console.log('ParentComponent onMount')
    await loadItems()
  })

  async function loadItems() {
    console.log('loadItems started:')
    const response = await getItemsFromApi()
    const data = await response
    items = data['items']
    console.log('loadItems completed:', { items })
  }

  function getItemsFromApi() {
    return new Promise((resolve, reject) => {
      setTimeout(
        resolve({
          items: [
            { text: 'Item 1', value: 'item_1' },
            { text: 'Item 2', value: 'item_2' },
            { text: 'Item 3', value: 'item_3' },
          ],
        }),
        Math.random() * 1000
      )
    })
  }
</script>

<ChildComponent label="ChildComponent w/o other text field:" {items} {value} />

ChildComponent.svelte:

<script>
  import { onMount } from 'svelte'

  export let label = ''
  // format: { text:, value: }
  export let items = []
  export let value = ''

  onMount(() => {
    console.log('ChildComponent onMount')
  })

  $: options = buildItems()

  function buildItems() {
    let tempItems = [{ text: 'Please select...', value: '' }, ...items]
    return tempItems
  }

  console.log('ChildComponent items:', { items })
</script>

<div>
  <div>{label}</div>
  <select>
    {#each options as option (Math.random())}
      <option value={option.value} selected={value === option.value}>{option.text}</option>
    {/each}
  </select>
</div>

This is the log output for when the code runs:

ChildComponent items: {items: Array(0)}
ChildComponent.svelte:15 ChildComponent onMount
ParentComponent.svelte:9 ParentComponent onMount
ParentComponent.svelte:14 loadItems started:
ParentComponent.svelte:18 loadItems completed: {items: Array(3)}

I’m expecting the ChildComponent to rerender because in the loadItems I set items which should trigger the ParentComponent to rerender.

What am I doing wrong?

>Solution :

Your reactive statement has no changing dependencies:

$: options = buildItems()

This will only run once. Svelte will not check the function contents.

Just inline the function:

$: options = [
  { text: 'Please select...', value: '' },
  ...items,
]

If you need multiple statements, you can also declare blocks or use an IIFE:

let options = [];
$: {
  // [do stuff]

  options = stuff;
}
$: options = (() => {
  // [do stuff]

  return stuff;
})()

Or declare the function reactively:

$: options = buildItems()
$: buildItems = function() { ... }
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading