Network
useFetcher
A hook to fetch data from an API endpoint. It also takes care of the loading and error states.
Parameters
Name | Type | Description |
---|---|---|
initialUrl | string | The initial URL to fetch data from. |
options | object | The options object to pass to the fetch API. |
Return Values
Name | Type | Description |
---|---|---|
response.data | any | The response data returned by the API endpoint. |
response.error | null | string | The error message if the request fails, otherwise null. |
response.loading | boolean | A boolean indicating if the request is in progress. |
response.url | string | The current URL of the API endpoint. Starts as the initial URL passed to the hook, but can be changed by updating this value at any time. Updating this value will trigger a new request. |
Source Code
export const useFetcher = (initialUrl, options) => { let url = $state(initialUrl); let data = $state(null); let loading = $state(true); let error = $state(); const setLoading = (isLoading = true) => { loading = isLoading; if (isLoading === true) { error = null; data = null; } }; const fetchData = async () => { try { const res = await fetch(url, options); if (!res.ok) throw new Error(`Unexpected error occurred (status ${res.status})`); const data = await res.json(); return [null, data]; } catch (e) { const { errorMessage = 'Unexpected error eccurred' } = e; return [errorMessage, null]; } }; const handleUrlChange = async (currentUrl) => { setLoading(true); const [err, response] = await fetchData(); if (currentUrl !== url) return; if (err) { setLoading(false); error = err; return; } setLoading(false); data = response; }; $effect(() => { handleUrlChange(url); }); return { get data() { return data; }, get loading() { return loading; }, get error() { return error; }, get url() { return url; }, set url(newUrl) { if (url !== newUrl) url = newUrl; }, }; }; export default useFetcher;
https://official-joke-api.appspot.com/jokes/1
Joke Seed
1
<script> import useFetcher from './useFetcher.svelte'; const endpoint = 'https://official-joke-api.appspot.com/jokes/'; const DEFAULT_URL = endpoint + 1; const DEFAULT_SEED = 1; let seed = $state(DEFAULT_SEED); let url = $derived(endpoint + seed); const response = useFetcher(DEFAULT_URL); $effect(() => { response.url = url; }); </script> <div class="container"> <span class="fetch-url">{response.url} </span> <div class="joke-header"> <span class="title">Joke Seed</span> <div class="seed-options"> <button type="button" on:click={() => seed--}>-</button> <span class="seed">{seed}</span> <button type="button" on:click={() => seed++}>+</button> </div> </div> <div class="joke"> {#if response.loading} <span class="spinner" /> {:else if response.error} <span class="error">{response.error}</span> {:else} <span class="joke-setup">{response.data.setup}</span> <span class="joke-punchline">{response.data.punchline}</span> {/if} </div> </div> <style> .container { display: flex; flex-direction: column; align-items: center; gap: 20px; } .fetch-url { background-color: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.05); padding: 10px 15px; border-radius: 5px; color: rgba(255, 255, 255, 0.7); cursor: not-allowed; } .joke-header { display: flex; flex-direction: column; gap: 10px; text-align: center; } .joke-header .title { text-transform: uppercase; letter-spacing: 1px; font-size: 13px; font-weight: 700; } .seed-options { display: flex; gap: 10px; } .seed-options button { font-size: 22px; line-height: 1; } .seed-options .seed { padding: 10px 20px; background-color: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.05); border-radius: 5px; } .joke { min-height: 70px; display: flex; flex-direction: column; justify-content: center; text-align: center; gap: 15px; } .joke .error { color: #f14d4d; font-weight: 600; font-size: 16px; } .joke-setup { font-size: 17px; font-weight: 600; color: rgba(255, 255, 255, 0.6); } .joke-punchline { font-style: italic; font-size: 20px; } </style>