Dansekongen
Dansekongen

Reputation: 338

Updating a route when re-navigation button is clicked in nav in Sapper

I have a navbar that includes a search bar, and it works just fine if the route is not currently the route of the search bar. However, I want to be able to do search while inside the route, but the component won't re-run.

I'm currently running with onMount, but have tried beforeUpdate and afterUpdate. I have also tried to just have it in the script as functions, but it's not updating.

The data is first passed to a store from the searchbar, then the new route picks it up from the store. This might not be ideal, but I didn't find a way to pass it down since it lies in the navbar.

Here is the code for the searchbar, I tried with replaceState: True, but that didn't seem to do anything.

async function sendSearchTerms(terms) {
    await goto("recipe", { replaceState: true });
    searchItems.set(terms);
}

Here is the code in the route it redirects to.

let unsubscribe;
let allIngredients;
let allRecipes;
let error;

onMount(async () => {
    unsubscribe = searchItems.subscribe((items) => {
        allIngredients = items;
    });

    const ingredients = fixTextArray(allIngredients);
    console.log(ingredients);
    if (ingredients) {
        try {
            const res = await fetch("dev_data/recipes.json");
            if (res.status == 200) {
                allRecipes = await res.json();
            } else {
                error = res.status;
            }
        } catch (err) {
            alert(err);
        }
    } else {
        console.log("CRAP");
    }
});

onDestroy(() => {
    if (unsubscribe) {
        unsubscribe();
    }
});

The result is fine as I mentioned if I'm searching from another route, so my initial thought was to replace onMount, as it is already mounted. But that did nothing, so I'm guessing Sapper kinda holds the state somewhere, to not force reloads of the same routes.

Also maybe there might be something in the markup that does this, so I'm including that as well:

{#if error}
    <h1>{error}</h1>
{:else}
    {#if allRecipes}
        <div class="cards">
            {#each allRecipes as recipe}
                <Card title={recipe.title} link="/{recipe.id}" />
            {:else}Loading{/each}
        </div>
    {:else}No recipes{/if}
{/if}

Upvotes: 0

Views: 396

Answers (1)

Stephane Vanraes
Stephane Vanraes

Reputation: 16411

You can move the code that fetches the recipes out of the onMount and set it as a separate reactive block instead:

let unsubscribe;
let allIngredients;
let allRecipes;
let error;

$: allIngredients && getRecipes(allIngredients)

const getRecipes = (allIngredients) => {
    const ingredients = fixTextArray(allIngredients);
    console.log(ingredients);
    if (ingredients) {
        try {
            const res = await fetch("dev_data/recipes.json");
            if (res.status == 200) {
                allRecipes = await res.json();
            } else {
                error = res.status;
            }
        } catch (err) {
            alert(err);
        }
    } else {
        console.log("CRAP");
    }
}

onMount(async () => {
    unsubscribe = searchItems.subscribe((items) => {
        allIngredients = items;
    });
})

onDestroy(() => {
    if (unsubscribe) {
        unsubscribe();
    }
});

The line $: allIngredients && getRecipes(allIngredients) will execute when allIngredients changes, if it is falsy it will stop, otherwise it will run getRecipes with it and get the recipes, updating the other variables.

Upvotes: 1

Related Questions