0
<script>
    let numbers = [1, 2, 3, 4];
    let obj = {
        foo:{
            bar: "bar"
        }
    }
    function addNumber() {
        numbers.push(numbers.length + 1);
        numbers = numbers;
    }
    
    const changeToBaz = (tempObj) => {
        tempObj.foo.bar = 'bazz';
    }

    $: sum = numbers.reduce((t, n) => t + n, 0);
    $:{
        console.log('sum: ', sum);
        console.log('obj: ', obj);
        changeToBaz(obj);
      }
</script>

<p>{numbers.join(' + ')} = {sum}</p>
<p>{obj.foo.bar}</p>

<button on:click={addNumber}>
    Add a number
</button>

I want to know why the obj.foo.bar is showing bazz ? I am passing the obj state to changeToBaz function and changing the value of bar property. I thought that by changing the value of tempObj.foo.bar will not trigger render as I am not doing obj = tempObj , and I also want to know why in the console the value of obj.foo.bar = "bazz" ,the console is before the calling of changeToBazz function

Here is the screenshot of the page
Page screenshot

Arix
  • 17
  • 3
  • 1
    The svelte 'immutable' [option](https://svelte.dev/docs#template-syntax-svelte-options) is 'false' by default so it will render on property changes. As for the console showing `bazz` it depends on which console and when you expand the object; objects are often live in the console and only resolve their property values when expanded, since you are calling `changeToBaz()` directly after the `log` call there is no opportunity for you to expand it before the value has already been changed, you could log a copy `console.log('obj: ', structuredClone(obj));` – pilchard Apr 23 '23 at 11:45

3 Answers3

1

The svelte immutable option is false by default so it will trigger reactivity on property changes so long as you tell it the object has changed (as you are doing with numbers = numbers;).

If you set the immutable option to true (<svelte:options immutable={true}/>) you will see no updates at all because svelte will now only use referential equality to trigger reactivity and since numbers = numbers; passes the same reference a re-render won't be triggered.

In both cases bazz is displayed from the very start because all reactive statements are run once before the initial render so while the initial console log of the object will show bar (see below for console caveats) the property will already be set to bazz when the first render takes place.


The MDN Svelte documentation has more description than the official documentation, see: Advanced Svelte: Reactivity, lifecycle, accessibility

Here are a few REPLs showing the options:

Reactivity example (immutable true) (no renders triggered on click)

Reactivity example (immutable true) forced update (render is forced by assigning a shallow copy numbers = [...numbers];)


As for the console showing bazz it depends on which console and when you expand the object; objects are often live in the console and only resolve their property values when expanded, since you are calling changeToBaz() directly after the log call there is no opportunity for you to expand it before the value has already been changed. You could log a copy console.log('obj: ', structuredClone(obj)); which would preserve the value at the time of logging. see: Is Chrome’s JavaScript console lazy about evaluating objects?

pilchard
  • 12,414
  • 5
  • 11
  • 23
  • By adding `` to the example in my answer I'm still reproducing the reported behaviour. – Al.G. Apr 23 '23 at 12:03
  • I'm not questioning whether the reactive statements run on page load, but whether they trigger a re-render or not. – pilchard Apr 23 '23 at 12:08
0

I did some digging in reactivity.
 I think the flow is something like this :


1. let obj = {
        foo:{
            bar: "bar"
        }
    } // causes the reactivity
    
    

2.  $:{
 
        console.log('sum: ', sum);
        console.log('obj: ', obj);
        changeToBaz(obj);
    
      } // since this block is dependent on obj , sum so the 
        // changeToBaz(obj) runs
    
    

3.  const changeToBaz = (tempObj) => {
        tempObj.foo.bar = 'bazz'; //  not causes reactivity but simply  
                                  // changes the bar property value to 
                                  // “bazz”
    }

4.   Since all the reactive statements are done, the component markup is 
    rendered and “bazz” is shown in UI.

Correct me if I am wrong.


Arix
  • 17
  • 3
  • For the sequence prior to the first render that is accurate enough. Things get more complicated on future event based updates (see my answer) – pilchard Apr 23 '23 at 19:33
-2

I want to know why the obj.foo.bar is showing bazz ?

To answer this question I trimmed your example to a MCVE and concluded that reactive statements run on page load:

<script>
    let y = "bar" // equivalent to your `obj` definition
    $: y = "bazz" // equivalent to your $: { changeToBaz(obj) } block
</script>

{y} <!--- outputs "bazz" - equivalent to your `<p>{obj.foo.bar}</p>` --->

That is, it does not matter whether you made an update to a nested value of the object or not.

Al.G.
  • 4,327
  • 6
  • 31
  • 56
  • 1
    By using a primitive value you bypassed all the possible issues in the OP's question. The issue here is svelte's handling of immutability which doesn't come in to play with primitive values. – pilchard Apr 23 '23 at 11:58
  • Then how do you explain the reactive block `$: y = "bazz"` being evaluated on page load without any other assignments? My point is that the block is evalued on page load, no matter what it *does* (assigning primitive values or nested object values). – Al.G. Apr 23 '23 at 12:01
  • Yes but the initial log should still be `bar` and with `immutable=true` no further renders will be triggered. – pilchard Apr 23 '23 at 12:05
  • A copy of my code in https://svelte.dev/repl/hello-world?version=3.58.0 plus the tag with `immutable=true` renders "bazz" on my screen and not "bar". – Al.G. Apr 23 '23 at 12:09
  • Yes, I've already agreed that the reactive statements run once before initial render, but you'll note that clicking the button shows no change nor any new logs on click with immutable set to true. – pilchard Apr 23 '23 at 12:12
  • Okay, I agree now. I just noted that within their long sentences OP is asking a second question, too, and my answer's only to the "Why do I see bazz?" part (which, I still believe, is correct). – Al.G. Apr 23 '23 at 12:20