Reputation: 23
I am writing a SPA Svelte app with svelte-navigator
. I want to fetch a request when pathname
is changed.
import { useLocation } from "svelte-navigator";
import { useCallApi } from "./hook";
const loc = useLocation();
const [resp, fetchPageData] = useCallApi(); // my `hooks` to call api
$: pathname = $loc.pathname;
$: loading = $resp.loading;
$: data = $resp.data;
$: {
console.log("loading changed", loading);
}
$: if (pathname) {
console.log(pathname);
fetchPageData(); // when pathname change, call a api
}
fetchPageData
is called when pathname
changed everytime, but the reactive declarations($: loading
) does not update everytime.
My way to use hooks
looks like this:
import { writable } from 'svelte/store';
export function useCallApi() {
let result = writable({
loading: false,
data: 0,
});
const fakeFetch = async () => {
result.update(pre => ({
data: 0,
loading: true,
}));
await new Promise(r => setTimeout(r, 500));
result.update(pre => ({
data: Math.random(),
loading: false,
}));
};
return [result, fakeFetch];
}
The online demo is https://svelte.dev/repl/e367c0aa3c9743cb8a749f92f0239d59?version=3.32.1 .
When click To a
or To b
to change the pathname
, reactive declarations update only once.
But when click the button trigger request
, all state update likes what I expected.
I want to know what is wrong with this, svelte
, svelte-navigator
or my hooks
? And why?
I would be most grateful if you could help me out.
updated: 2021-02-02
My friend found that the statements order
affects the update behavior, which looks somewhat like the link. If put fetchPageData()
to the top, the update behavior looks good.
import { useLocation } from "svelte-navigator";
import { useCallApi } from "./hook";
const loc = useLocation();
const [resp, fetchPageData] = useCallApi();
// the order is important which will affect the update behavior
$: if (pathname) {
console.log(pathname);
fetchPageData();
}
$: pathname = $loc.pathname;
$: loading = $resp.loading;
$: data = $resp.data;
$: {
console.log("loading changed", loading, $resp.loading);
}
Upvotes: 1
Views: 321
Reputation: 2679
I don't really know why but when you replace the auto-subscription $
by resp.subscribe()
it works :
import { useLocation } from "svelte-navigator";
import { useCallApi } from "./hook";
const loc = useLocation();
const [resp, fetchPageData] = useCallApi(); // my `hooks` to call api
$: pathname = $loc.pathname;
let loading;
let data;
resp.subscribe( result => {
loading = result.loading;
data = result.data;
});
$: {
console.log("loading changed", loading);
}
$: if (pathname) {
console.log(pathname);
fetchPageData(); // when pathname change, call a api
}
You could try it here : https://svelte.dev/repl/ca065fdc188e41938b8e3db0b8c61f20?version=3.32.1
I think the answer is behind the way svelte executes reactive statements.
According to : https://stackoverflow.com/a/63977760/12153710 :
svelte batches all the changes to update them in the next update cycle, and before it updates the DOM, it will execute the reactive declarations to update the reactive variables.
When you update your router from a , I think that compiled code waits the promise to resolve (in hook.js
) before updating the reactive statement $: loading = $resp.loading;
and $: data = $resp.data;
and therefore the { loading }
and { data }
for the DOM.
Upvotes: 1