Skip to content

Partially raw state (shallow proxy) #16516

@rChaoz

Description

@rChaoz

Describe the problem

State is great to automatically track all deep changes made to data and perform precise (fine-grained) updates based on these changes. But, it comes with downsides, such as performance issues with large data due to proxies, and the fact that you can no longer perform equality checks.

To get around this, $state.raw() exists. But it, too, has downsides: you can no longer track deep changes to data, for example, you can no longer use regular JS like Array.push(), like you do with regular state. An example of such a use case, is when you have a large array of immutable objects, for example table rows:

<script>
    let data = $state([])

    // data is manipulated in many ways, like
    data.push(a, b, c)
    data.splice(n, 1)
    data[5] = replaced
</script>

<!-- display data with pagination etc. -->

If there is too much data (thousands of elements), you will encounter massive performance issues due to the proxies. For this reason, $state.raw() was added, but using this means you have to update the code that you use to modify the data:

data = [...data, a, b, c]
data.splice(n, 1)
data = [...data] // can't even do data = data, like in Svelte 4
data[5] = replaced
data = [...data]

A better option so you don't have to copy the array each time might be to use a store, which allows updating without changing the value reference. Regardless, you still lose fine-grained updated, i.e. data[5] causes the entire {@each} to rerun, for example.

Describe the proposed solution

Allow proxifying data partially. For example, to solve the case above:

let data = $state.shallow([])

This proxifies the array itself, but not any of the inner elements, so you can still get fine-grained updates based on the array itself, just like you'd get for an array of primitives, like numbers. Now, this is just a public API suggestion, but if $state were to be allowed in object/array literals, this could be used like:

let data = $state({
    config: {...} // deeply reactive,
    items: $state.shallow([]), // array items are not proxied
    map: $state.shallow({}), // also works with objects used as key/value pairs
    other: $state.raw(...), // fully opt-out of proxies for this specific property
})

Note that this is similar to, but not the same as #15689; specifically, a major point of this is $state.shallow() (or whatever syntax it may be, like$state(..., { depth: 1 })), for arrays or key/value pairs.

Importance

would make my life easier

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions