Monday, May 20, 2024
 Popular · Latest · Hot · Upcoming
58
rated 0 times [  65] [ 7]  / answers: 1 / hits: 11726  / 4 Years ago, thu, september 17, 2020, 12:00:00

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?


More From » svelte

 Answers
5

@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


[#2666] Sunday, September 13, 2020, 4 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
daquanmilesw

Total Points: 57
Total Questions: 102
Total Answers: 110

Location: Wallis and Futuna
Member since Sat, Aug 6, 2022
2 Years ago
daquanmilesw questions
Sat, Jul 10, 21, 00:00, 3 Years ago
Wed, Jun 24, 20, 00:00, 4 Years ago
Sun, Dec 8, 19, 00:00, 5 Years ago
;