3

I'm developing a gantt chart component in Svelte and it consists of 100s of row components each containing multiple task components each. Tasks can be dragged and moved. Performance is a priority and I am using a virtual list to render only the visible rows.

<div class="scrollable">
    {#each visibleRows as row (row.id)}
        <div class="row">
            {#each row.tasks as task (task.id)}
                <Task from={task.from} to={to}/>
            {/each}
        </div>
    {/each}
</div>

The problem is when I move tasks around and scroll rows out of the view, they get destroyed, and when I scroll back to them, their state gets reset. This REPL using svelte-virtual-list illustrates the problem https://svelte.technology/repl?version=2.13.5&gist=bdb4c523d2e1cf7e3aef452e3f24d988 I am wondering what is the best way to deal with this situation.

Currently I'm using the task object as prop and updating it, the reference keeps the same and scrolling doesn't affect it.

<Task {task}/>

But inside Task I update state like this

const {task} = this.get();
task.from = new Date();
this.set({task});

and the template references every prop with task.propname and I'm not sure if it's the svelte way of doing it as it is not clear what exactly gets set.

I could bind task variables like this

<Task bind:from=task.from 
      bind:to={task.to}/>

But a lot of variables need to be bind (more than 10), and I want to allow possible future plugins update new props from inside the Task component.

<Task {task} 
      {...task}/>

This deals with potential bigger number of props, but I update the task object in the state event like this:

onstate({ changed, current, previous }) {
    const {task} = this.get();
    Object.assign(task, current);
    this.set({task});
},

Which of these should I prefer for performance or is there a better way to handle this?

Ante Novokmet
  • 388
  • 3
  • 13
  • 1
    Seems like you have no choice to use onstate event. What problems are you having with using the onstate event? – Hassan Voyeau Oct 05 '18 at 11:33
  • Any updates on this? – Hassan Voyeau Oct 08 '18 at 15:59
  • 1
    I'll keep using the first approach, but rename the `task` prop to _task, _data, or _state to further distinguish it as some kind of "protected state" property. The last approach with `onstate` fires on every state change, even those that shouldn't end up updating `task`, and it's easy to end up with unnecessary data in it... – Ante Novokmet Oct 09 '18 at 08:00

1 Answers1

3

You should alter the array of data instead of mutating the object. By mutating the object inside the array, Svelte does not know that your array has changed.

Mutating the array would go something like this with Svelte v3 and using a store instead of plain-array:

// data.js
...
import { writable } from 'svelte/store';

export default writable(getData())
...

// App.svelte
<script>
...
import items from './data.js';

function updateContent(item) {
    const index = $items.indexOf(item)
    $items = [
        ...$items.slice(0, index),
        { ...item, content: 'updated content' },
        ...$items.slice(index + 1)
    ]
}
...
</script>

<VirtualList items={$items} let:item bind:start bind:end>
    ...
        <button on:click={() => updateContent(item)}>update content</button>
    ...
</VirtualList>

Full example: https://svelte.dev/repl?version=3.0.0-beta.28&gist=f8c7e96bb34906db2c3a5e85b4840017

You can read more about updating arrays and objects here: https://svelte.dev/tutorial/updating-arrays-and-objects

arve0
  • 3,424
  • 26
  • 33
  • In your example, where is `items` being instantiated in `data.js`, it's not, right? And how come you call on `$items` in `updateContent` instead `items`? – user82395214 Dec 13 '19 at 03:04