Make AJAX Request from Store
Asked Answered
W

1

8

I have 3 questions regarding Svelte Stores:

  1. How do I make ajax request inside a store? I've tried using the following:

REPL Demo

//store.js

import { writable } from 'svelte/store';

let data = [];

const apiURL = "https://jsonplaceholder.typicode.com/todos";

async function getData(){
    const response = await fetch(apiURL);
    data = (await response.json()).slice(0,20);
    console.log('Response:', data);
}
getData();

export const testsStore = writable(data);

The request goes trough but the data never gets passed to the export. All the examples I've seen use static data without async/await. I've also tried return data; and writable(getData()); but it return a promise and not the data itself.

  1. Is this even the right way of loading data from API into a store or should I make the call somewhere else.

  2. How and when do I use export default testsStore; I tried using it from another example and it throws saying that store.js isn't exporting testsStore

Welter answered 12/11, 2020 at 6:32 Comment(0)
S
15

Since it's a writable store, you can call set or update on it to change the data (see docs).

For example:

import { writable } from 'svelte/store';

const apiURL = "https://jsonplaceholder.typicode.com/todos";

async function getData(){
    const response = await fetch(apiURL);
    const data = (await response.json()).slice(0,20);
      testStore.set(data) // <================================
}
getData();

export const testStore = writable([])

However, it seems that this specific use case would be better served by a readable store. A readable store takes its initial value as first argument and a "lifecycle" function as its second argument. The lifecyle function receives a set function to change the store value, but the store itself doesn't expose set or update methods (hence it's not writable from the outside).

For example:

import { readable } from 'svelte/store';

const apiURL = "https://jsonplaceholder.typicode.com/todos";

const getData = async () => {
    const res = await fetch(apiURL)
    if (!res.ok) throw new Error('Bad response')
    const items = await res.json()
    return items.slice(0, 20)
}

export const todos = readable([], set => {
    // called when the store is first subscribed (when subscribers goes from 0 to 1)
    getData()
        .then(set)
        .catch(err => {
            console.error('Failed to fetch', err)
        })
    return () => {
        // you can do cleanup here if needed
    }
})

Finally, in .svelte components, you can prefix stores with a $ to directly access their value. With this notation, Svelte will automatically subscribe to the store when needed, and unsubscribe from it when the component is destroyed.

So in your example, using our readable todos store above, you can change your component to simply this:

<script>
  import { todos } from './store.js';
</script>

<h1>Todos:</h1>

{#each $todos as item}  
  <p>{item.title}</p>
{/each}
Shimmer answered 12/11, 2020 at 7:55 Comment(3)
Great that works. Thanks for the explanation. Also found out that you can handle the promise by wrapping it into {#await} block in the .svelte file. See this exampleWelter
While you can have a store return a promise and use {#await}, you might find this to get a little tedious vs. resolving the promise then setting the readable store's value as rixo suggested in this answer.Janaye
Thank you for the concise explanation of async store setup. The problem I was having was completely resolved using your post as a direct template!Oquendo

© 2022 - 2024 — McMap. All rights reserved.