Making Destructured Props Reactive in Svelte

Originally published on 24 May, 2021 by Jacob Stordahl

I was working on a Sapper app last week and ran into a weird bug that I had never come across before. When navigating to a dynamic route (/poems/[slug]), the app would navigate to the first poem normally, but further navigation to any sibling routes would cause the route to change in the browser, but the DOM would not be updated.

This is because I recently changed how I fetch data in Sapper/Typescript apps, particularly from Sanity. I've started returning a single object from a context=module because it keeps the props coming into the component very intuitive readable. It's also much easier to create a custom type for the props if there is one object being passed down. I can then destructure the props into the individual variables which allows the template to be very intuitive as well. However, there's one crucial detail I missed.

Here's the code that caused the error in a dynamic route file called [slug].svelte...

<script context="module" lang="ts">
  import type { Preload } from "@sapper/common"
  import { client, urlFor } from '../../components/SanityClient'

  export const preload:Preload = async ({ params: { slug } }) => {
    const query = `*[slug.current == "${ slug }"]`
    const res = await client.fetch(query)
    const poem = await res.shift()
    return { poem }
  };
</script>

<script lang="ts">
  import type { Image, Block } from '@sanity/types'
	
  type Slug = {
    _type: string,
    current: string,
  };
  interface MainImage extends Image {
    alt: string,
  }
  export let poem: { slug: Slug, name: string, content:Array<Block>, background:Array<Block>, backgroundTitle:string, poemImage:MainImage, _createdAt:string, _updatedAt:string};	
	
  const { slug, name, content, background, backgroundTitle, poemImage, _createdAt, _updatedAt } = poem;
</script>

Most of this code is clean, however the last line spoils it and cost me several hours of debugging. This assignment of our destructured values to our poem prop is not reactive. This is precisely what caused the bug I described above. To solve this, you simply need to convert the destructured line into a Svelte reactive statement.

<script>
...

$: ({ slug, name, content, background, backgroundTitle, poemImage, _createdAt, _updatedAt } = poem);

...
</script>

This will ensure the destructured values will be redefined anytime the prop changes.

© 2021 Jacob Stordahl | built with Steel