当函数中的变量发生变化时,不会触发 Svelte 反应性 [英] Svelte reactivity not triggering when variable changed in a function
问题描述
我在这里有点困惑,不幸的是我无法在 svelte 的不和谐频道上找到任何解决方案,所以我走了......
我有一个相当基本的两个类的例子,让它们成为 App
和 Comp
.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:
<输入类型=文本"绑定:值={输入值}>
所以一旦值改变:
- 反应式
if (rendered) {...}
条件被调用 updateInputValue
被调用,inputValue
被改变.HTML 输入元素更新为此值.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);
第一条语句依赖于
validate
和inputValue
,第二条语句依赖于rendered
、updateInputValue
和value
,声明按原样保留.
现在,了解了反应式声明的这 2 种行为,让我们来看看您的 REPL.>
当您更改inputValue
、rendered
或value
时,Svelte 将批量更改,并开始新的更新周期.
就在更新 DOM 之前,Svelte 将在 1 go 中执行所有响应式声明.
因为 validate(inputValue);
和 if (rendered) updateInputValue(value);
语句之间没有依赖关系,(如前所述),它们将被执行顺序.
如果你只改变 rendered
或 value
,第一条语句 (validate(inputValue)
) 不会被执行,同样,如果您更改 inputValue
第二条语句 (if (rendered) updateInputValue(value)
) 将不会被执行.
现在,在 updateInputValue
中,您更改了 inputValue
的值,但由于我们已经处于更新周期中,因此我们不会开始新的周期.
这通常不是问题,因为如果我们按照依赖顺序对反应式声明进行排序,更新依赖变量的语句将在依赖变量的语句之前执行.
所以,知道出了什么问题,有一些解决方案"你可以去.
- 手动重新排序反应式声明语句,尤其是当执行顺序存在隐式依赖时.
请参阅此 REPL
因此将您的 REPL 更改为:
$: if (rendered) {更新输入值(值);}$:验证(输入值);
- 在响应式声明中明确定义依赖
$: validate(inputValue);$:如果(渲染){输入值 = 更新输入值(值);}函数更新输入值(val){console.log('updateInputValue 被调用!');如果(!值){返回 '';}别的 {返回值;}}
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:
- The reactive
if (rendered) {...}
condition is called updateInputValue
is called andinputValue
is changed. HTML input element is updated to this value.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 ondouble
. 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
andinputValue
, 2nd statement depends onrendered
,updateInputValue
andvalue
, 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.
- 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);
- 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;
}
}
这篇关于当函数中的变量发生变化时,不会触发 Svelte 反应性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!