Search code examples
websveltesveltekitsvelte-3svelte-component

onMount called after function call from parent component


I have the following minimal example:

Component A:

<script lang="ts">
  import {tick} from 'svelte';
  import LocationSelector from './LocationSelector.svelte';
  
  let locationSelector: LocationSelector;
  let selectedCompany: string;

  async function updateAvailableLocations(){
    await tick();
    locationSelector.setAvailableLocations(selectedCompany);
  }

  $: selectedCompany, updateAvailableLocations();
</script>

<input  type="text"  bind:value={selectedCompany} />
{#if selectedCompany}
  <LocationSelector bind:this={locationSelector}/>
{/if}

LocationSelector.svelte component:

<script lang="ts">

  let availableLocations: Locations[];

  export function setAvailableLocations(type: string) {
          for (const location of availableLocations ) {
              if(company == location.company) {
                  .... (Code to set the available locations in the dropdown)
              }
             
          }
  }
    
  onMount(async () => {
    try {
      availableLocations = await api.getAllLocations();
    } catch (error) {
      consolo.log("...")
    }
  
  }
</script>
...

Problem

My problem is that when the selectedCompany value is going from undefined or and empty string to a value and therefore the "if" is true the LocationSelector.svelte component will be shown but the onMount function of the LocationSelector.svelte will be called after the setAvailableLocations function. Which means the availableLocation is empty and therefore useless.

Why is setAvailableLocations called after the onMount in this case?


Solution

  • I wrote an even more minimal example that demonstrates that the onMount callback gets called before the method. Check the box, look in the console, and see for yourself.

    The reason availableLocations is empty in your example is because await api.getAllLocations() hasn't finished yet. onMount doesn't make any guarantee about async callbacks running to completion.

    The way I'd fix this is to make selectedCompany a prop rather than a value that must be passed to a method, then use a reactive statement that checks when selectedCompany and availableLocations are both ready:

    $: if (selectedCompany && availableLocations.length) {
        setAvailableLocations(selectedCompany);
    }