Home > Enterprise >  Why isn't my svelte {#each} block reactive?
Why isn't my svelte {#each} block reactive?

Time:04-22

So basically I am playing with Svelte trying to spin up a quick app, details of the app aren't important but basically it hosts a bunch of embedded sites. see example here & for replicability:

https://svelte.dev/repl/6f3484554ef8489b9a5960487a0a1f95?version=3.47.0

My problem is that when I add a new url & title to the sites list, the {#each} block that creates the embedded views doesn't update to reflect the new state of the list, even though the list is clearly updating in the console output. Is it something to do with scope or is it a Svelte issue of not triggering reactivity on prop reassignments from components?

Update: some sites don't allow embedding so use https://wikipedia.org as a safe one for testing.


if you replace a hard-coded url in the sites list with wiki address it should work fine. i basically want a new window to pop up as the {#each} block creates a new SiteView component

CodePudding user response:

If you want to change a value from another component you need to bind the property, otherwise the relationship is one-way only (from parent component to child).

<InputBar bind:sites {site}/>

CodePudding user response:

There are several things wrong with your code, the first being that you do not propagate the changes made to the sites array back to the main application, you should use bind: to keep the two arrays in sync.

<InputBar bind:sites {site} />

The second is that you are modifying an object when adding a new site and then adding that object to the array, this will always be the same object so if you change it the previously added sites will also change. You can solve this by spreading the new object into the array instead:

function add() { sites = sites.concat({...site}); console.log(sites)}
// or alternatively
function add() { sites = [...sites, {...site}]; console.log(sites); }

That said, the application is not very "Svelte" like as it mixes responsibilities and exposes data to components that don't need that data. For example, why would the input bar need to know about the current sites ? It would be a lot better to have the input bar be just that, an input bar. When the user clicks 'add' it raises an event that says 'something has been added' and resets the fields. Then the parent is responsible to add it to the array. This will make for a more flexible solution. If you do that you will see there is also no reason to have a 'site' variable on the top level (or even have that object at all, you can just have two fields)


<script>
    import { createEventDispatcher } from 'svelte'
    let url = ''
    let title = ''
    const dispatch = createEventDispatcher()
    function add() {
        dispatch('add', { url, title })
        url = ''
        title = ''
    }
</script>
<div >
    <p>Enter a site to stream:</p>
    <input type="text"  placeholder="www.example.com" bind:value={url}>
    <br>
    <input type="text" placeholder="example" bind:value={title}>
    <button on:click={add}>add</button>
</div>
<InputBar on:add={(ev) => sites = [...sites, ev.detail]} />

On a final note, to add things to the head of the html use <svelte:head> instead.

  • Related