1
votes

I'm running into an issue trying to delete an item from a store. I have a route /view/:id and am using that id to access a value from a custom writable store (items). I wrote a simple selector method to retrieve the item data based on the id, and have created a reactive $: item variable to listen for changes (items can be edited in this view). Here are the details from my <script>

// ItemView.svelte
export let params: { id?: string } = {};
import { push } from "svelte-spa-router";
import { items } from "./stores/items";

$: [item] = items.select($items, params.id);

function handleDelete() {
  if (confirm("Are you sure you want to delete this item?")) {
    items.delete(item.id);
    push("/");
  }
}

When I call handleDelete, I get the following error:

Uncaught (in promise) TypeError: can't access property "type", ctx[0] is undefined
    update bundle.js:3796
    update index.mjs:764
    flush index.mjs:732
    promise callback*schedule_update index.mjs:707
    make_dirty index.mjs:1442
    ctx index.mjs:1477
    7 bundle.js:3898
    set index.mjs:35
    update index.mjs:43
    delete items.ts:59
    handleDelete ItemView.svelte:17

If I wrap items.delete(item.id) in a timeout, then everything works (5ms did the trick), but that doesn't seem right. I don't have a lot of experience with Svelte, but it feels like the issue is with the reactive variable unsubscribing. Should I be getting the single item data differently? Do I need to manually unsubscribe from the store before navigating?

I tried calling unsubscribe manually, but that didn't work either:

export let params: { id?: string } = {};
import { push } from "svelte-spa-router";
import { items } from "./stores/items";
import { onDestroy } from "svelte";

let item;

const unsubscribe = items.subscribe(
  (value) => (item = items.select(value, params.id)[0])
);

function handleDelete() {
  if (confirm("Are you sure you want to delete this item?")) {
    unsubscribe(); // <- Here
    items.delete(item.id);
    push("/");
  }
}

onDestroy(unsubscribe);

That gave me the following error:

Uncaught (in promise) TypeError: stop is not a function
    subscribe index.mjs:58
    run index.mjs:18
    run_all index.mjs:24
    destroy_component index.mjs:1431
    update bundle.js:918

I'm not committed to any of the code I posted; I just want to be able to view a single item, and delete it and navigate away.

1
could you also post your store code ?Stephane Vanraes
Maybe you are making it more complicated then necessary. Try this to get the item $: item = $items.find(o => o.id == params.id); and this to delete it $items = $items.splice($items.findIndex(o => o.id == params.id), 1);Molda
@Molda That looks like it would work. My select function provides some additional ordering utilities that I would prefer not to lose.SeanMcP

1 Answers

0
votes

I learned that ctx in the error message referred to the right-hand side of the reactive variable assignment. In this instance, that was the return from my select method: items.select($items, params.id).

ctx[0] is the first item in that array, or the variable that I created by destructuring: $: [item].

So when Svelte said ctx[0] is undefined, it meant that there was no first entry in the ctx array; item was undefined (because it had been deleted).

I thought there was something important about the property "type", but it ended up just being the first key on item that I was trying to access in my markup: {item.type}. Trying to access any property would have failed.

By wrapping everything in an {#if item} condition, I was able to resolve the error:

<!-- ItemView.svelte -->
{# if item}
<p>Type: {item.type}</p>
{/if}

I wouldn't recommend any of the above as a good pattern to follow. But if you are looking to resolve a similar issue, consider this solution.