I am trying to get my head around the svelte 3 reactivity thing...
I wanted to force refreshing a UI on a button click. I am using a custom component AsyncFetcher
that accepts HTTP post data, and returns data
object (http post result) for its slot.
I wanted to have a disable functionality. So when the "Disable" button is clicked an http api is called followed by a refresh of the data view.
<script>
export let id
function onDisable() {
fetch('disable-api-url', {id: id})
// Then ??
// What to do after the fetch call, to refresh the view
}
</script>
<AsyncFetcher postParam={id} let:data>
{data.name}
<button on:click={??}>Refresh</button>
<button on:click={onDisable}>Disable Item</button>
</AsyncFetcher>
I tried doing on:click={() => id=id}
to trick it to refresh to no avail. If id
would have been an object rather than string id={...id}
would have worked, which unfortunately, is not the case here.
What would be a correct way to achieve this?
Using a component to manage fetches is very unconventional. Typically you'd fetch data inside onMount
, or in an event handler:
<script>
import { onMount } from 'svelte';
let initialData;
let otherData;
onMount(async () => {
const res = await fetch('some-url');
initialData = await res.json();
});
async function update() {
const res = await fetch('some-other-url');
otherData = await res.json();
}
</script>
{#if initialData}
<p>the data is {initialData.something}</p>
{/if}
<button on:click={update}>update</button>
To fetch data use the await block:
<script>
async function fetchData() {
const res = await fetch('/api')
const data = await res.json
if (res.ok) {
return data
} else {
throw new Error(data)
}
}
</script>
<style>
.error {
color: red;
}
</style>
{#await fetchData}
<p>Fetching...</p>
{:then data}
<div>{JSON.stringify(data)}</div>
{:catch error}
<div class="error">{error.message}</div>
{/await}
To refresh the data you need to trigger a rerender by updating a piece of related state, since this will rerun the await block. You can trigger a rerender by storing the fetch function in a piece of state and reassigning it when the refresh button is clicked:
<script>
async function fetchData() {
const res = await fetch('/api')
const data = await res.json
if (res.ok) {
return data
} else {
throw new Error(data)
}
}
let promise = fetchData()
</script>
<style>
.error {
color: red;
}
</style>
<button on:click="{() => {promise = fetchdata()}}">Refresh</button>
{#await promise}
<p>Fetching...</p>
{:then data}
<div>{JSON.stringify(data)}</div>
{:catch error}
<div class="error">{error.message}</div>
{/await}
While Rich Harris gives a completely serviceable answer, here's a solution for forcing Svelte to update a component to reflect an external change of its data (also posted here).
main.js; vanilla from the examples online, no special changes:
import App from './App.svelte';
var app = new App({
target: document.body
});
export default app;
index.html; Note window.neek = {...}
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Svelte app</title>
<script>
window.neek = { nick: true, camp: { bell: "Neek" }, counter: 0 };
</script>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>
App.svelte; Note $: notneek = window.neek
and window.neek.update = ...
:
<script>
let name = 'world';
$: notneek = window.neek;
function handleClick() {
notneek.counter += 1;
}
window.neek.update = function () {
notneek = notneek;
}
</script>
<h1>Hello { notneek.camp.bell }!</h1>
<button on:click={handleClick}>
Clicked {notneek.counter} {notneek.counter === 1 ? 'time' : 'times'}
</button>
Since the update
function is within the scope of App.svelte
, it is able to force the re-render when called via window.neek.update()
. This setup uses window.neek.counter
for the internal data utilized by the button (via notneek.counter
) and allows for the deep properties (e.g. neek.camp.bell = "ish"
) to be updated outside of the component and reflected once neek.update()
is called.
In the console, type window.neek.camp.bell = "Bill"
and note that Hello Neek!
has not been updated. Now, type window.neek.update()
in the console and the UI will update to Hello Bill!
.
Best of all, you can be as granular as you want within the update
function so that only the pieces you want to be synchronized will be.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With