Skip to content
Advertisement

Nested component does not render properly in Svelte/Sapper

I have three files inside a slug. I use slug parameters as directory name. The problem I am having is everything except the each loop in taglist. For some reason it does not receive the prop tagList. Any help would be appreciated.

index.svelte

<script context="module">
    export function preload({ params }, { user }) {
        let [id, slug] = [params.id, params.slug];
        return { id, slug };
    }
</script>

<script>
    import Editor from "../../../_components/Editor.svelte";
    import Topics from "./Topics.svelte";
    import { stores } from "@sapper/app";

    export let id;
    export let slug;

    const { session } = stores();
</script>

<svelte:head>
    <title />
</svelte:head>

<div class="editor-page">
    <div class="container page">
        <div class="row">
            <div class="col-md-8 offset-md-2 col-xs-12">
                <Topics {id} {slug} />
                {#if $session.user}
                    <Editor />
                {/if}
            </div>
        </div>
    </div>
</div>

Topics.svelte

<script>
    import { onMount } from "svelte";
    import * as api from "api.js";
    import "bytemd/dist/index.min.css";
    import TagList from "../../../_components/_TagList.svelte";

    export let id;
    export let slug;

    let topics = [];
    let title = "";
    let tagList = [];
    let value = "";
    let Viewer = null;

    onMount(async () => {
        const bytemd = await import("bytemd");
        Viewer = bytemd.Viewer;

        const response = await api.get(
            `t/${id}/${slug}`,
            localStorage.getItem("jwt")
        );

        console.log(response);

        if (response.topic) {
            topics = response.topic;
            title = response.title;            
            value = topics[0].description;
            for(let i= 0; i < response.tags.length; i++) {
                tagList.push(response.tags[i]);
            }
        }
    });
</script>

<div>
    <h3>{title}</h3>
    <hr/>
    <svelte:component this={Viewer} {value} />
    <TagList {tagList} />
</div>

_TagList.svelte

<script>
    export let tagList;

    console.log(tagList);
</script>

<ul>
    {#each tagList as tag}
        <p>hello</p>
        <li>{tag.name}</li>
    {/each}
</ul>

Advertisement

Answer

In Svelte, updates are only triggered with an assignment.

In your case that means that when the component is rendered it will render an empty taglist (tagList = []).

Now in onMount you do taglist.push, but as said earlier, this doesn’t trigger an update (remember that this function is called after the component has mounted) because it is not an assignment.

There are four ways to fix it in your case:

  1. after the for loop you do tagList = tagList, this is an assignment and will trigger the update.
  2. instead of doing the for loop use a mapping tagList = response.tags.map(tag => tag)
  3. instead of doing the for loop you spread the tags into the taglist tagList = [...response.tags]
  4. considering you don’t do anything with the tags anyway, and tagList is empty and you don’t seem to have any other way to update, just assign the tags to it directly tagList = response.tags

Of course your code might be simplified, if you actually do something with each tag before adding it to the list case 3 and 4 are not good options, for that scenario I would use the map option

8 People found this is helpful
Advertisement