【问题标题】:Svelte - $: reactive statement not workingSvelte - $:反应性语句不起作用
【发布时间】:2021-06-18 22:19:13
【问题描述】:

我正在尝试为我的苗条应用程序构建一个队列。基本上,我的服务器发送消息,它们需要按顺序执行,每个动作都等待最后一个完成。我目前使用的模型是这样的: 我建立了一个商店来存储我的 actionQueue

import { writable, derived } from "svelte/store";
import type { Action } from "../types/Actions";

const initialState: Action[] = [];

let id = 0;

function createActionQueue() {
  const { subscribe, update } = writable<Action[]>(initialState);

  return {
    subscribe,
    add: (payload: Action) => {
      update((queue) => [...queue, { id: id++, ...payload }]);
    },
    next: () => {
      update((queue) => {
        queue.shift();
        return queue;
      });
    },
  };
}

export const actionQueue = createActionQueue();
export const nextAction = derived(
  actionQueue,
  ($actionQueue) => $actionQueue[0]
);

在需要监听 actionQueue 的组件上,我会这样做

  $: doNextAction($nextAction);

  function doNextAction(nextAction: Action) {
    if (nextAction) {
      if (nextAction.type === "someType") {
        doSomething();
    actionQueue.next();
      } else if (nextAction.type === "anotherType") {
        doSomethingElse();
    actionQueue.next();
      }
    }
  }

但有时,actionQueue.next() 不会触发 $nextAction 的更新。我通过在随机组件上使用此命令验证了这一点:

$: console.log('nextAction',$nextAction);

我也尝试过直接使用 $actionQueue[0] ,但仍然不太奏效。我错过了什么?

【问题讨论】:

    标签: svelte svelte-3 svelte-store


    【解决方案1】:

    循环依赖。

    我使用-&gt; 来表示“一个触发器另一个的变化”,然后 dep 图如下:

    actionQueue.add() -> $nextAction -> doNextAction($nextAction) 
    -> actionQueue.next() -> $nextAction -> doNextAction($nextAction) -> ...
    

    如您所见,$: doNextAction($nextAction) 基本上会自行触发。为了防止无限循环,svelte 运行时只会触发一次并停在那里。因此,您看到的行为。

    我整理了一个更简单的演示来说明这个过程。

    <script>
    let counter = 0
    function add() { counter += 1 }
    function next(x) { counter = x * 2 }
    $: next(counter)
    </script>
    
    <h1>counter: {counter}!</h1>
    <button on:click={add}>+1</button>
    

    基本上add() 映射到您的actionQueue.add()next() 映射到doNextAction()

    虽然next()确实改变了counter,但不会触发另一轮$: next(counter)

    解决方案

    如果你想绕过这个限制(它实际上是保护),你可以通过使用setTimeout 来欺骗 svelte 运行时来监督循环依赖。

    function doNextAction(nextAction: Action) {
      if (nextAction) {
        if (nextAction.type === 'someType') {
          doSomething()
          // the trick:
          setTimeout(() => actionQueue.next())
        } else if (nextAction.type === 'anotherType') {
          doSomethingElse()
          // the trick:
          setTimeout(() => actionQueue.next())
        }
      }
    }
    

    【讨论】:

      【解决方案2】:

      我认为问题在于您的情况下无法访问语句actionQueue.next(),因为属性nextAction.type 是未定义的,因此即使您将新项目添加到队列中,控制台总是会打印相同的第一个元素

      我已经创建了一个 REPL,它看起来运行顺利,你可以看看这个link

      【讨论】:

      • 到达命令actionQueue.next(),每次都触发,没有到达命令不是问题。如果我在 actionQueue.next() 定义中添加一个 console.log,我可以看到队列正在更新。但是,即使我看到队列正在更新,有时 $actionNext 也不会触发更新(使用 queue.shift(),人们会期望数组的第一个元素发生变化)。
      • 我注意到当有 20 个动作被添加到队列中时,触发器没有发生,每个动作都需要大约 100 毫秒才能完成,有时它会卡在一个动作上并且不会t完成触发它们。
      猜你喜欢
      • 2021-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-09
      • 2012-02-29
      相关资源
      最近更新 更多