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?
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() { ... }