Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making class instance reactive in Svelte using stores

I am learning Svelte by creating simple app.

The logic is written using classes. The idea is, that all the data needed comes from class instance properties. Instances should not be instantiated more than once. I am using stores to provide components this instances.

The problem is I can't get reactivity using this approach. I tried readable and writable stores and nothing helps. It is still possible to get reactivity using OOP and what can I do? Reassignment and creating new instances will be expensive.

Edit

I can't make up the example in REPL cause the class is too big.

Parser.js

export default class Parser {
  constructor() {
    this._history = [];
  }

  parse(string) {
    this._history.push(string)
  }

  get history() {
    return this._history;
  }
}

Here I pass instance to the store.

parserStore.js

import writable from "svelte/store";
import Parser from "Parser.js"

export const parserStore = writable(new Parser());

In this component I get the instance and use reactively a method.

Component_1.svelte*

import { parserStore } from "parserStore.js";

$: result = parserStore.parse(binded_input_value);

What I want to get is the up to time history property that was updated from using class method:

Component_2.svelte

import { parserStore } from "parserStore.js";

$: history = parserStore.history;

{#each history as ... }

I know, it is not the best example, but what I want is reactive class instance available through the store. Actually the values are up to date, but it is not causing the re-render of the components. When the component is mounted - data of the latest, but after nothing re-renders at all even so the properties of the instance is changed.

like image 649
Eric Rovell Avatar asked Jul 13 '20 21:07

Eric Rovell


People also ask

Are svelte stores reactive?

Svelte also provides a very intuitive way to integrate stores into its reactivity system using the reactive $store syntax. If you create your own stores honoring the store contract, you get this reactivity syntactic sugar for free.

How do you make a variable reactive in svelte?

We can tell Svelte that we want our totalTodos and completedTodos variables to be reactive by prefixing them with $: . Svelte will generate the code to automatically update them whenever data they depend on is changed. Note: Svelte uses the $: JavaScript label statement syntax to mark reactive statements.

How do I update a writable store svelte?

Click the stores. js tab to see the definition of count . It's a writable store, which means it has set and update methods in addition to subscribe . Clicking the + button should now update the count.


1 Answers

Short answer

As far as I know you cannot do this, this way.

Longer answer

There might be, depending on some factors (like preferences, existing libraries, etc...), ways around it.

Solution 1: use stores in the class

The first and most straightforward one is to use stores in the class itself:

Parser.js

import { writable } from 'svelte/store'

class Parser {
  constructor() {
    this._history = writable([])
  }

  parse(string) {
        console.log(string)
    this._history.update(v => [...v, string])
  }

  get history() {
    return this._history;
  }
}

parserStore.js

import { Parser } from './Parser.js'¨

export const parser = new Parser()

Component1.svelte

<script>
    import { parser } from './parserStore.js';

    let value
    let { history } = parser
    
    $: parser.parse(value);
</script>

<input bind:value />

{#each $history as h}<p>{h}</p>{/each}

Notice how only the history part of this class would be a store.

Solution 2: Rewrite using Custom Store

This approach is, in essence, very close to the previous one but is slightly more common in the Svelte Community. It technically just wraps the build in stores to get some extra functionality.

parserStore.js

import { writable } from 'svelte/store'

export const parser = (() => {
    const P = writable([])  
    const { set, subscribe, update } = P    
    
    function parse(string) {
        P.update(arr => [...arr, string])
    }
    
    return {
        parse,
        subscribe
    }
})()

Component1.svelte

<script>
    import { parser } from './parserStore.js';

    let value
    $: parser.parse(value)
</script>

<input bind:value />

{#each $parser as h}<p>{h}</p>{/each}

Note that here there is not history property anymore, you iterate straight over parser, if you still want the history property you have to adjust the code slightly:

parserStore.js

  ...
  return {
    parse,
    history: { subscribe }
  }

Component1.svelte

<script>
  ...
  const { history } = parser
  ...
</script>

{#each $history as h}<p>{h}</p>{/each}
like image 127
Stephane Vanraes Avatar answered Sep 19 '22 03:09

Stephane Vanraes