gpt4 book ai didi

How to trigger/force update a Svelte component(如何触发/强制更新Svelte组件)

转载 作者:bug小助手 更新时间:2023-10-25 14:21:43 31 4
gpt4 key购买 nike



I am trying to get my head around the svelte 3 reactivity thing...

我在试着把我的头脑弄清楚关于苗条3反应的事情...




  1. 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.


  2. 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.

我尝试在上执行:Click={()=>id=id}欺骗它刷新,但无济于事。如果id是对象而不是字符串,id={...id}就可以工作,不幸的是,这里不是这样。



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:

使用组件来管理FETCH是非常非常规的。通常,您将在onmount内部或在事件处理程序中获取数据:



<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>


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).

虽然Rich Harris给出了一个完全可用的答案,但这里有一个强制Svelte更新组件以反映其数据的外部更改的解决方案(也在这里发布)。



main.js; vanilla from the examples online, no special changes:

Main.js;来自在线示例的普通内容,没有特殊更改:



import App from './App.svelte';

var app = new App({
target: document.body
});

export default app;


index.html; Note window.neek = {...}:

Index.html;备注window.nek={...}:



<!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 = ...:

App.svelte;注$:notnek=window.nek and window.nek.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.

因为更新函数在App.svelte的作用域内,所以当通过window.nek.update()调用时,它能够强制重新呈现。此设置将window.nek.count用于按钮使用的内部数据(通过notnek.count),并允许在组件外部更新深层属性(例如nek.camp.bell=“ish”),并在调用nek.update()时反映这些属性。



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!.

在控制台中,键入window.nek.camp.bell=“Bill”并注意Hello Neek!尚未更新。现在,在控制台中输入window.nek.update(),用户界面将更新为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.

最棒的是,您可以在更新功能中随心所欲地进行粒度调整,以便只同步您想要同步的片段。



To fetch data use the await block:

要获取数据,请使用AWAIT块:


<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:

要刷新数据,您需要通过更新一段相关状态来触发重新呈现,因为这将重新运行等待块。您可以通过将FETCH函数存储为一种状态并在单击刷新按钮时重新分配它来触发重现器:


<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}


Here's a somewhat hacky solution for forcing a rerender of a component not dependent on external data:

以下是一个有点老套的解决方案,用于强制重新呈现不依赖外部数据的组件:


<script>
// Await immediately resolved promise to react to value change.
const forceUpdate = async (_) => {};
let doRerender = 0;
</script>
{#await forceUpdate(doRerender) then _}
<ForcedToRerender on:click={() => doRerender++} />
{/await}


I tried to find a more "native" solution, but this is what I ended up with.
REPL: https://svelte.dev/repl/2dc5c7ca82bc450f8f7dd25d2be577b1?version=3.43.0

我试图找到一个更“本地化”的解决方案,但这就是我最终得到的结果。记者:https://svelte.dev/repl/2dc5c7ca82bc450f8f7dd25d2be577b1?version=3.43.0



I did this (made the component disappear and reappear with a timer):

我这样做了(使组件消失,并使用计时器重新出现):


<script>
import ForcedToRerender from './ForcedToRerender.svelte'
let visible = true
let rerender = () =>
{
visible=false
setTimeout(()=>{visible = true}, 100)
}
</script>
{#if visible}
<ForcedToRerender />
{/if}
<button on:click={rerender}>Rerender</button>

ForcedToRerender.svelte:

ForcedToRerender.svelte:


<script>
import { onMount } from 'svelte'
let num = 0
let rnd = () => num = Math.random()
onMount(rnd)
</script>
<div on:click={rnd}>
{num}
</div>

This works, as you can see here.

这是可行的,正如您在这里看到的。



in my case, svelte did not flush the output,

cos i blocked the javascript event loop by running a benchmark at 100% cpu

在我的例子中,svelte没有刷新输出,因为我通过在100%的CPU上运行基准测试来阻止了javascript事件循环


in this case, the trick is to manually unblock the event loop with await sleep(10)

在这种情况下,诀窍是使用等待睡眠(10)手动取消阻塞事件循环


<script>
function sleep(millisec = 0) {
return new Promise((resolve, reject) => {
setTimeout(_ => resolve(), millisec);
});
};
let result = '';
async function runBenchmark() {
for (let step = 0; step < 10; step++) {

// this needs 100% cpu, so no time for svelte render
cpuburn(); result += `${step}: 1.234 sec\n`;

// unblock the JS event loop, so svelte can render
await sleep(10);
}
}
</script>

<pre>{result}</pre>

here is a repl (but currently it triggers a bug in the repl runtime)

下面是一个Repl(但目前它在Repl运行时中触发了一个错误)


solving this with synchronous function calls is probably not possible

(something like a $$svelte.forceTickSync())

使用同步函数调用可能无法解决这个问题(类似于$$svelte.forceTickSync())



The key (no pun intended) to make a component fully reload, and not just update the inside values, is to use {#key value_to_watch} like:

使组件完全重新加载而不仅仅是更新内部值的关键(不是双关语)是使用{#key Value_to_watch},如下所示:


{#key category_on}
<Testone a={category_on} />
{/key}

If category_on changes the <Testone/> component is fully reloaded
REPL

如果CATEGORY_ON更改,组件将完全重新加载REPL


更多回答

Wow! this was an awesome answer. I just refactored my code from onMount() to this and it worked right away. Such a more beautiful solution. Thank you!

哇!这是一个很棒的回答。我刚刚将我的代码从onmount()重构为这个,它立刻就起作用了。这样一个更美好的解决方案。谢谢!

This worked perfectly for my use case. I had a list of items and I wanted to do something on a ctrl+click, but on OSX that brings up a context menu, so I had to use on:contextmenu|preventDefault but then my UI did not update, even though the state was being set. Using this and adding a doRerender++ to my contextmenu handler works perfectly.

这对我的用例非常有效。我有一个项目列表,我想做一些事情上的一个按钮+点击,但在OSX上,带来了一个上下文菜单,所以我不得不使用on:contextmenu| preventDefault但是我的UI没有更新,即使状态被设置了。使用这个并添加一个doRenender ++到我的contextmenu处理程序工作得很好。

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

您的答案可以通过其他支持信息来改进。请编辑以添加更多详细信息,如引用或文档,以便其他人可以确认您的答案是正确的。你可以在帮助中心找到更多关于如何写出好答案的信息。

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com