功能中的变量更改时,不触发苗条的反应性

时间:2020-09-17 08:52:40

标签: javascript svelte svelte-3

我在这里有点困惑,很不幸,我在Svelte的不和谐频道上找不到任何解决方案,所以我在这里...

我有两个类的相当基本的示例,让它们分别为AppCompApp创建一个Comp实例,然后在单击按钮后更新该实例的value

Comp实例应将此值设置为其他变量(inputValue),并在更改该变量后应触发validate(inputValue)这是反应性的。这是一个REPL:https://svelte.dev/repl/1df2eb0e67b240e9b1449e52fb26eb14?version=3.25.1

App.svelte:

<script>
    import Comp from './Comp.svelte';
    
    let value = 'now: ' + Date.now();
    
    function clickHandler(e) {
        value = 'now ' + Date.now();
    }
</script>

<Comp
    bind:value={value}
/>
<button type="button" on:click={clickHandler}>change value</button>

比较苗条:

<script>
    import { onMount } from 'svelte';

    export let value;

    let rendered = false;
    let inputValue = '';

    $: validate(inputValue); // This doesn't execute. Why?

    function validate(val) {
        console.log('validation:', val); 
    }

    onMount(() => {
        rendered = true;
    });

    $: if (rendered) {
        updateInputValue(value);
    }

    function updateInputValue(val) {
        console.log('updateInputValue called!');
        if (!value) {
            inputValue = '';
        }
        else {
            inputValue = value;
        }
    }
</script>

<input type="text" bind:value={inputValue}>

因此,一旦更改值:

  1. 反应性if (rendered) {...}条件称为
  2. 调用
  3. updateInputValue并更改inputValue。 HTML输入元素将更新为该值。
  4. validate(inputValue)从不对这一更改做出反应-为什么?

如果我在反应性updateInputValue条件中省略了对if (rendered)函数的额外调用,并将updateInputValue函数体的代码直接置于条件中,则validate(inputValue)被正确触发,即:

// works like this  
$: if (rendered) {
    if (!value) {
        inputValue = '';
    }
    else {
        inputValue = value;
    }
}

那么在功能中更新后为什么不起作用?

3 个答案:

答案 0 :(得分:4)

@johannchopin的answer揭示了这个问题。


您可以阅读my blogpost for slightly in-depth explanation of how reactive declaration works,这是tl; dr:

  • 反应性声明批量执行

    svelte分批处理所有更改以在下一个更新周期中对其进行更新,并且在更新DOM之前,它将执行反应式声明以更新反应式变量。

  • 反应性声明按其依赖项的顺序执行

    反应式声明是批量执行的,每个声明执行一次。一些声明是相互依赖的,例如:

    let count = 0;
    $: double = count * 2;
    $: quadruple = double * 2;
    

    在这种情况下,quadruple取决于double。因此,无论您的反应式声明的顺序是$: double = count * 2;之前的$: quadruple = double * 2还是相反,前者应在之前执行。

    苗条的将按照相关性顺序对声明进行排序

    在彼此之间没有依赖关系的情况下:

    $: validate(inputValue);
    $: if (rendered) updateInputValue(value);
    

    第一条语句取决于validateinputValue,第二条语句取决于renderedupdateInputValuevalue,声明按其顺序保留是。


现在,了解反应式声明的这两种行为,让我们来看看您的REPL

在更改inputValuerenderedvalue时,Svelte将批量更改,并开始新的更新周期。

在更新DOM之前,Svelte会一口气执行所有反应式声明。

由于validate(inputValue);if (rendered) updateInputValue(value);语句之间没有依赖关系(如前所述),它们将按顺序执行。

如果仅更改renderedvalue,将不会执行第一条语句(validate(inputValue)),同样,如果更改inputValue,第二条语句({ {1}})将不会执行。

现在,在if (rendered) updateInputValue(value)中,您可以更改updateInputValue的值,但是由于我们已经处于更新周期,因此我们不会启动新的值。

这通常不是问题,因为如果我们按照相关性顺序对反应式声明进行排序,则更新依赖变量的语句将在依赖变量的语句之前执行。


因此,知道出了什么问题后,您可以寻求一些“解决方案”。

  1. 手动重新排列反应式声明语句的顺序,尤其是在执行顺序存在隐式依赖性的情况下。

在此REPL

中查看对反应式声明进行排序的区别

因此将您的REPL更改为:

inputValue

See REPL

  1. 在反应式声明中明确定义依赖项
$: if (rendered) {
  updateInputValue(value);
}
$: validate(inputValue);

See REPL

答案 1 :(得分:1)

真的很奇怪(我无法真正解释它),但是如果您将反应式语句ERROR: column "delta_sec" does not exist LINE 5: where delta_sec > 10000 ^ SQL state: 42703 Character: 125 放在函数$: validate(inputValue);声明之后,它将按预期工作:

updateInputValue

选中此REPL

答案 2 :(得分:0)

以下是原始帖子问题和一些变通方法的最小示例:

<script>
  let nb
  let n=0
$: console.log("nb1:",nb)
$: update(n)
$: console.log("nb2:",nb)
function update(v) {
  console.log("update",v)
  nb = v
}
</script>
<h1 on:click={()=>n=Math.floor(Math.random()*100)}>click: {nb}</h1>
<h1 on:click={()=>update(Math.floor(Math.random()*100))}>click: {nb}</h1>

nb分配了新值并且由于分配而运行n时,不会触发$: update(n)的第一个日志。 nb的第二个日志正在运行,因为它是在分配给nb之后。
如果直接调用update函数(最后一个h1),一切正常。

如果您在最高级别为nb分配了一个值,那么一切也将起作用:

 <script>
      let nb
      let n=0
    $: console.log("nb1:",nb)
    $: nb = update(n)
    $: console.log("nb2:",nb)
    function update(v) {
      console.log("update",v)
      return v
    }
    </script>
    <h1 on:click={()=>n=Math.floor(Math.random()*100)}>click: {nb}</h1>
    <h1 on:click={()=>update(Math.floor(Math.random()*100))}>click: {nb}</h1>

我相信这是一个错误。