当函数中的变量发生变化时,不会触发 Svelte 反应性 [英] Svelte reactivity not triggering when variable changed in a function

查看:36
本文介绍了当函数中的变量发生变化时,不会触发 Svelte 反应性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在这里有点困惑,不幸的是我无法在 svelte 的不和谐频道上找到任何解决方案,所以我走了......

我有一个相当基本的两个类的例子,让它们成为 AppComp.App 创建一个 Comp 实例,然后在单击按钮后更新此实例的 value.

Comp 实例应该将此值设置为不同的变量 (inputValue),并且在更改该变量时,它应该触发 validate(inputValue) 这是反应性的.这是一个 REPL:https://svelte.dev/repl/1df2eb0e67b24426e67b2440e67b2440e67b240e63b1240e52e52fb26eb14?version=3.25.1.1

App.svelte:

<比较绑定:值={值}/><按钮类型=按钮"on:click={clickHandler}>更改值</button>

Comp.svelte:

<输入类型=文本"绑定:值={输入值}>

所以一旦值改变:

  1. 反应式 if (rendered) {...} 条件被调用
  2. updateInputValue 被调用,inputValue 被改变.HTML 输入元素更新为此值.
  3. validate(inputValue) 永远不会对这种变化做出反应 - 为什么?

如果我在响应式 if (rendered) 条件中省略了对 updateInputValue 函数的额外调用,并将 updateInputValue 函数体的代码直接放入条件,然后 validate(inputValue) 被正确触发,即:

//像这样工作$:如果(渲染){如果(!值){输入值 = '';}别的 {输入值 = 值;}}

那么为什么在函数中更新时它不起作用?

解决方案

@johanchopin 的 answer 揭示了这个问题.


您可以阅读我的博文,了解一下-对反应式声明如何工作的深入解释,这里是 tl;dr:

  • 响应式声明批量执行.

    svelte 将所有的更改批量化以在下一个更新周期更新它们,并且在更新 DOM 之前,它会执行反应性声明来更新反应性变量.

  • 响应式声明按依赖项的顺序执行.

    reactive 声明是批量执行的,每个声明执行一次.一些声明是相互依赖的,例如:

    let count = 0;$: double = 计数 * 2;$: 四倍 = 双倍 * 2;

    在这种情况下,quadruple 依赖于 double.所以无论你的反应式声明的顺序如何, $: double = count * 2;$: quadruple = double * 2 之前,或者相反,前者应该是和在后者之前执行.

    Svelte 将按依赖顺序对声明进行排序.

    在相互之间没有依赖关系的情况下:

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

    第一条语句依赖于 validateinputValue,第二条语句依赖于 renderedupdateInputValuevalue,声明按原样保留.


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

当您更改inputValuerenderedvalue 时,Svelte 将批量更改,并开始新的更新周期.

就在更新 DOM 之前,Svelte 将在 1 go 中执行所有响应式声明.

因为 validate(inputValue);if (rendered) updateInputValue(value); 语句之间没有依赖关系,(如前所述),它们将被执行顺序.

如果你只改变 renderedvalue,第一条语句 (validate(inputValue)) 不会被执行,同样,如果您更改 inputValue 第二条语句 (if (rendered) updateInputValue(value)) 将不会被执行.

现在,在 updateInputValue 中,您更改了 inputValue 的值,但由于我们已经处于更新周期中,因此我们不会开始新的周期.

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


所以,知道出了什么问题,有一些解决方案"你可以去.

  1. 手动重新排序反应式声明语句,尤其是当执行顺序存在隐式依赖时.

请参阅此 REPL

因此将您的 REPL 更改为:

$: if (rendered) {更新输入值(值);}$:验证(输入值);

查看 REPL

  1. 在响应式声明中明确定义依赖

$: validate(inputValue);$:如果(渲染){输入值 = 更新输入值(值);}函数更新输入值(val){console.log('updateInputValue 被调用!');如果(!值){返回 '​​';}别的 {返回值;}}

查看 REPL

I am a bit confused here and unluckily I couldn't get any solution on the discord channel of svelte so here I go...

I have a rather basic example of two classes, let them be App and Comp. App creates a Comp instance and then updates this instance's value after a button click.

The Comp instance should set this value to a different variable (inputValue) and upon changing that variable it should fire validate(inputValue) which is reactive. Here is a 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>

Comp.svelte:

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

So as soon as the value is changed:

  1. The reactive if (rendered) {...} condition is called
  2. updateInputValue is called and inputValue is changed. HTML input element is updated to this value.
  3. validate(inputValue) never reacts to this change - WHY?

If I omit the extra call to the updateInputValue function in the reactive if (rendered) condition and put updateInputValue function body's code directly to the condition, then validate(inputValue) is triggered correctly, i.e.:

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

So how come it doesn't work when updated in the function?

解决方案

@johannchopin's answer has revealed the issue.


You can read my blogpost for slightly in-depth explanation of how reactive declaration works, here's the tl;dr:

  • reactive declarations are executed in batch.

    svelte batches all the changes to update them in the next update cycle, and before it updates the DOM, it will execute the reactive declarations to update the reactive variables.

  • reactive declarations are executed in order of their dependency.

    reactive declarations are executed in batch, and each declaration is executed once. some declarations are depending on each other, eg:

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

    in this case, quadruple depends on double. so regardless of order of your reactive declarations, $: double = count * 2; before $: quadruple = double * 2 or the other way round, the former should be and will be executed before the latter.

    Svelte will sort the declarations in the dependency order.

    In cases where there's no dependency relationship with each other:

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

    1st statement depends on validate and inputValue, 2nd statement depends on rendered, updateInputValue and value, the declaration is left in the order as it is.


Now, knowing this 2 behaviors of reactive declaration, let's take a look at your REPL.

As you change inputValue, rendered or value, Svelte will batch the changes, and start a new update cycle.

Right before updating the DOM, Svelte will execute all the reactive declaration in 1 go.

Because there's no dependency between the validate(inputValue); and if (rendered) updateInputValue(value); statements, (as explained earlier), they will be executed in order.

If you change rendered or value only, the 1st statement (validate(inputValue)) will not be executed, and similarly, if you change inputValue the 2nd statement (if (rendered) updateInputValue(value)) will not be executed.

Now, in the updateInputValue you change the value of inputValue, but because we are already in an update cycle, we wont start a new one.

This usually isn't a problem, because if we sort reactive declarations in dependency order, the statement that updates the variable depended on will be executed before the statement that relies on the variable.


So, knowing what's wrong, there's a few "solutions" you can go for.

  1. Manually reorder the reactive declaration statements, especially when there's an implicit dependency of the order of execution.

See the difference of ordering the reactive declaration in this REPL

So change your REPL to:

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

See REPL

  1. Explicitly defining the dependency in the reactive declarations

$: validate(inputValue);

$: if (rendered) {
    inputValue = updateInputValue(value);
}
    
function updateInputValue(val) {
    console.log('updateInputValue called!');
    if (!value) {
        return '';
    }
    else {
        return value;
    }
}

See REPL

这篇关于当函数中的变量发生变化时,不会触发 Svelte 反应性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆