最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Svelte - how to wait for data that is being passed in from parent component? - Stack Overflow

programmeradmin0浏览0评论

I'm learning Svelte and I want to use data from one JSON API in three ponents. The data looks like this:

{
  "stats": {
    "currentYear": {
      "total": 6,
      "success": 6
    },
    "thirty": {
      "total": 30,
      "success": 28
    },
    "hundred": {
      "total": 100,
      "success": 92
    },
    "allTime": {
      "total": 789,
      "success": 728
    }
  },
  "heatmap": {
     ...
  },
  "other": {
     ...
  }
}

I retrieve the data via onMount in the App.svelte main ponent via async fetch, this works well. Then I want to pass each object to its corresponding ponent, so the stats object gets passed to Stats.svelte, the heatmap object to Heatmap.svelte etc.

To illustrate my issue, in Stats.svelte I am trying to display percentage values for each time period, for example:

  • current year: 100%
  • last thirty days: 93%
  • last 100 days: 92%
  • all time: 92%

Also, the CSS class for each will be based on some threshold values to change the colour (x >= 95: green, 95 > x >= 90: yellow, x < 90: red).

So some basic putation is needed which I wanted to have in a generic function, like shown below.

The stats object does get passed in from the parent ponent App.svelte, and if all I wanted to do is to show its values in the HTML via the {#await} block, this would work fine. However, I want to do some calculations, so I wanted to call a function that would use the stats object's data, but I do not know how to call this function at the right moment. Calling it on onMount does not work, because it's too early, the data ing in from the parent ponent has not yet been received.

<script>
    import { onMount } from "svelte"
    
    export let stats

    let currentYearClass, currentYearStat

    const calcPercentage = async (period) => {
        currentYearStat = stats[period].currentYearSuccess * 100 / stats[period].currentYearTotal
        currentYearClass = 'green'
    }

    onMount( async () => {
        calcPercentage('currentYear')
    })
</script>
<div id="stats">
{#await stats}
    <div>Waiting for stats ...</div>
{:then stats}
    <div class="{currentYearClass}" id="currentYear">{currentYearStat}</div>
    ...
    ...
{/await}
</div>

I'm learning Svelte and I want to use data from one JSON API in three ponents. The data looks like this:

{
  "stats": {
    "currentYear": {
      "total": 6,
      "success": 6
    },
    "thirty": {
      "total": 30,
      "success": 28
    },
    "hundred": {
      "total": 100,
      "success": 92
    },
    "allTime": {
      "total": 789,
      "success": 728
    }
  },
  "heatmap": {
     ...
  },
  "other": {
     ...
  }
}

I retrieve the data via onMount in the App.svelte main ponent via async fetch, this works well. Then I want to pass each object to its corresponding ponent, so the stats object gets passed to Stats.svelte, the heatmap object to Heatmap.svelte etc.

To illustrate my issue, in Stats.svelte I am trying to display percentage values for each time period, for example:

  • current year: 100%
  • last thirty days: 93%
  • last 100 days: 92%
  • all time: 92%

Also, the CSS class for each will be based on some threshold values to change the colour (x >= 95: green, 95 > x >= 90: yellow, x < 90: red).

So some basic putation is needed which I wanted to have in a generic function, like shown below.

The stats object does get passed in from the parent ponent App.svelte, and if all I wanted to do is to show its values in the HTML via the {#await} block, this would work fine. However, I want to do some calculations, so I wanted to call a function that would use the stats object's data, but I do not know how to call this function at the right moment. Calling it on onMount does not work, because it's too early, the data ing in from the parent ponent has not yet been received.

<script>
    import { onMount } from "svelte"
    
    export let stats

    let currentYearClass, currentYearStat

    const calcPercentage = async (period) => {
        currentYearStat = stats[period].currentYearSuccess * 100 / stats[period].currentYearTotal
        currentYearClass = 'green'
    }

    onMount( async () => {
        calcPercentage('currentYear')
    })
</script>
<div id="stats">
{#await stats}
    <div>Waiting for stats ...</div>
{:then stats}
    <div class="{currentYearClass}" id="currentYear">{currentYearStat}</div>
    ...
    ...
{/await}
</div>
Share Improve this question asked Jan 11, 2021 at 9:48 jfixjfix 5718 silver badges28 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

There are several ways to do this, but one way would be to have the calcPercentage take in the stats as an argument and then call it reactively.

export let stats
let currentYearClass, currentYearStat

const calcPercentage = (stats, period) => {
   currentYearStat = stats[persion}......
   currentYearClass = 'green'
}

$: stats && calcPercentage(stats, 'currentYear')

Edit: Some Explaining

The first problem with the original solution, something you probably noticed is that the ponent is mounted without the correct data, making stats to be undefined.

The above solution works in two steps:

$: stats && calcPercentage(stats, 'currentYear')

This first part defines a reactive statement, it will check if stats evaluates to true, something it will do unless it is undefined, false or 0. If stats is true it will then execute the function.

The second part is a the same function as before, I added the stats argument here although it is strictly not required in this situation, as the function will, because of the earlier execute everytime stats changes and is a true-like value.

With those two in place, when mounting the reactive statement will fail because stats is undefined, the function is not executed. Once the data es in, it will be re-evaluated, stats is no longer undefined and the function fires.

Extra

When the reactive statement would be of the form:

$: myfunction(myvar)

It will execute for every value change of myvar, even during the mounting (consider it going from non exisiting to undefined ?). This means you would have to move your check into the function itself, for some scenarios this might be desired, an example is where this actually part of an assignment and the function itself is defined outside the ponent

import heavyCalc from 'heavy/calc/function`

$: value = heavyCalc(otherValue)

You didn't show how stats is passed down to this ponent, but we can extrapolate two problems from your original solution:

  1. The stats prop isn't passed in right away, therefore stats is undefined during onMount.
  2. onMount is only executed when a ponent is mounted to the DOM (see documentation). If stats changes during the lifetime of the ponent calcPercentage will not be rerun.

You're looking for a way to run calcPercentage every time stats changes. You should use a reactive statement for this:

$: if (stats) calcPercentage('currentYear')

This reactive statement will run every time stats changes.

The if ensures that calcPercentage only runs if stats has a truthy value. undefined is falsy, so calcPercentage will not run as long as stats is undefined.
A more elegant (but less resilient) approach would be to only render your Stats ponent after the data has been loaded using {#await}, as described by @Ayoub Fiad's answer.

Please note that the Svelte piler does not know that calcPercentage depends on stats, only values which directly appear within the reactive statement will bee dependencies. In this case it knows to rerun when stats changes because stats is used directly in the if-block. The alternative for situations where you don't need such an if is to make calcPercentage take stats as a parameter, as described in @Stephane Vanraes' answer.

I'd propose to render the Stats.svelte ponent only after the data is loaded using {#await}:

  1. In App.svelte do this:

    {#await promise}
    
        <div>Loading...<div>
    
    {:then stats}
    
        <Stats {stats} />
    
    {/await}
    
  2. In Stats.svelte your data will be ready to use.

发布评论

评论列表(0)

  1. 暂无评论