Search code examples
sveltesvelte-5

Passing reactive props to children


I'm really struggling to understand reactivity in Svelte 5.

I've created a little class to encapsulate a data fetch operation:

class DataResponse {
  data = $state([]);
  error = $state();
  isLoading = $state(false);
}

And a useFetchData helper that kicks off a fetch and returns an instance of this class (which is updated by reassigning data when the response is received).

export function useFetchData() {
  const resp = new DataResponse();

  async function fetchData() {
    resp.isLoading = true;
    try {
      const response = await fetch("https://randomuser.me/api/?results=5");
      const j = await response.json();

      resp.data = j.results;
      resp.error = undefined;
    } catch (err) {
      resp.error = err;
      resp.data = undefined;
      console.error(err);
    } finally {
      resp.isLoading = false;
    }
  }

  fetchData();
  return resp;
}

If I use this data directly at the call site, reactivity works as I'd expect and the UI updates when the API call returns.

<script>
    import { useFetchData } from "./useFetchData.svelte.js";

    const resp = useFetchData();  
</script>

{#each response.data as person}
    <div>{person.name.first} {person.name.last}</div>
{/each}

When I extract the UI logic into a separate component and try to pass this data to a child component as a prop, I lose reactivity and the UI never updates. What am I doing wrong?

<script>
    import { useFetchData } from "./useFetchData.svelte.js";

    const resp = useFetchData();  
</script>

<People people={rest.data} />
<!-- People.svelte -->
<script>
    let people = $props();
</script>

<h1>People</h1>
{#each people as person}
    <div>{person.name.first} {person.name.last}</div>
{/each}

I've prepared a minimal example in the playground: https://svelte.dev/playground/455ad944b0cd42fdbee891a488ad372f?version=5.19.0


Solution

  • The props syntax in People is off, you get all props as the object. You probably intended it to be this:

    let { people } = $props();
    

    You can also get all props and access the property separately if you want:

    let props = $props();
    
    {#each props.people as person} ...