【问题标题】:How to trigger/force update a Svelte component如何触发/强制更新 Svelte 组件
【发布时间】:2019-11-15 09:17:57
【问题描述】:

我正在努力解决 svelte 3 反应性问题...

  1. 我想在单击按钮时强制刷新 UI。我正在使用一个自定义组件 AsyncFetcher,它接受 HTTP 发布数据,并为其插槽返回 data 对象(http 发布结果)。

  2. 我想要一个禁用功能。因此,当单击“禁用”按钮时,会调用 http api,然后刷新数据视图。

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

我尝试做on:click={() =&gt; id=id} 来欺骗它刷新无济于事。如果 id 是一个对象而不是字符串 id={...id} 会起作用,但不幸的是,这里不是这样。

实现这一目标的正确方法是什么?

【问题讨论】:

    标签: svelte


    【解决方案1】:

    要获取数据,请使用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}
    

    要刷新数据,您需要通过更新相关状态来触发重新渲染,因为这将重新运行等待块。您可以通过将 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}
    

    【讨论】:

      【解决方案2】:

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

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

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

      这行得通,你可以看到here

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      【解决方案3】:

      这是强制重新渲染不依赖于外部数据的组件的一个有点老套的解决方案:

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

      我试图找到一个更“原生”的解决方案,但这就是我最终得到的。 回复:https://svelte.dev/repl/2dc5c7ca82bc450f8f7dd25d2be577b1?version=3.43.0

      【讨论】:

        【解决方案4】:

        在我的情况下,svelte 没有刷新输出,
        因为我通过在 100% cpu 上运行基准测试来阻止 javascript 事件循环

        在这种情况下,诀窍是使用 await sleep(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 是一个 repl(但目前它会在 repl 运行时触发一个错误)

        用同步函数调用解决这个问题可能是不可能的
        (类似于$$svelte.forceTickSync()

        【讨论】:

          【解决方案5】:

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

          ma​​in.js;来自在线示例的香草,没有特殊变化:

          import App from './App.svelte';
          
          var app = new App({
              target: document.body
          });
          
          export default app;
          

          index.html;注意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;注意$: notneek = window.neekwindow.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>
          

          由于update 函数在App.svelte 的范围内,它能够在通过window.neek.update() 调用时强制重新渲染。此设置使用window.neek.counter 作为按钮使用的内部数据(通过notneek.counter),并允许在组件外部更新深层属性(例如neek.camp.bell = "ish")并在调用neek.update() 时反映。

          在控制台中,输入window.neek.camp.bell = "Bill" 并注意Hello Neek! 尚未更新。现在,在控制台中输入window.neek.update(),UI 将更新为Hello Bill!

          最重要的是,您可以在 update 函数中进行任意细化,以便只同步您想要同步的部分。

          【讨论】:

            【解决方案6】:

            使用组件来管理获取是非常规的。通常,您会在 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>
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-07-15
              • 2021-02-26
              • 1970-01-01
              • 1970-01-01
              • 2020-08-04
              • 2022-12-31
              • 1970-01-01
              • 2010-11-19
              相关资源
              最近更新 更多