Search code examples
sveltesveltekitsvelte-store

How should I build my store for complex object with methods?


My object is like:

const inventory = {
  maxSlots: 24,
  content: [
    {id: "item_01", quantity: 5, slotId: 0},
    {id: "item_02", quantity: 2, slotId: 2},
    {id: "item_03", quantity: 1, slotId: 6},
  ]
};

I need custom methods like:

  • inventory.getRemainingSlots()
  • inventory.getFirstFreeSlotid()
  • inventory.addItem() (this one depends on inventory.getFirstFreeSlotid())

How should I make this object reactive?

  • Single writable?
  • Custom store with get, set, update and subscribe methods?
  • Class instance with writable?
  • One writable for each single property, then a store of stores?

I've reached this point so far:

inventoryStore.js

function createStore() {
    const { subscribe, set, update } = writable({
        maxSlots: 8,
        content: [
            { id: "item_01", quantity: 5, slotId: 0 },
            { id: "item_02", quantity: 2, slotId: 2 },
            { id: "item_03", quantity: 1, slotId: 6 },
        ],
    });

    return {
        // Store methods
        subscribe,
        set,
        update,
        get: () => get({ subscribe }),

        // Custom methods
        getContent: () => {
            // I can't use this in .svelte files 
            return inventoryStore.get().content;
        },
        getFirstFreeSlotId: () => {
            const content = inventoryStore.get().content;
            const maxSlots = inventoryStore.get().inventoryMaxSlots;

            for (let _slotId = 0; _slotId < maxSlots; _slotId++) {
                const foundItem = content.find(_x => _x.slotId === _slotId);

                if (!foundItem?.id) {
                    return _slotId;
                }
            }

            return -1;
        }
    };
}

export const inventoryStore = createStore();


Solution

  • I figured it out:

    /stores/inventoryStore

    import { derived, writable } from "svelte/store";
    import { get } from "svelte/store";
    
    const inventoryStore = writable({
        toolbarMaxSlots: 8,
        inventoryMaxSlots: 24,
        /** @type {Array<{id: string, quantity: number, slotId: number}>} */
        content: [
            {id: "item_01", quantity: 5, slotId: 0},
            {id: "item_02", quantity: 2, slotId: 2},
            {id: "item_03", quantity: 1, slotId: 6},
        ],
    });
    
    export const inventory = {
        _store: inventoryStore,
        subscribe: inventoryStore.subscribe,
        set: inventoryStore.set,
        update: inventoryStore.update,
        get,
    
        addItem: function ({ id = "", quantity = 1 }) {
            return this.update((_prev) => {
                const newItem = {
                    id,
                    quantity,
                    slotId: 1, // todo: get first free slot
                };
                _prev.content = [..._prev.content, newItem];
    
                return _prev;
            });
        },
    
        // usable in js
        getItems: function () {
            return this.get(this._store).content;
        },
        
        // usable in svelte
        getDerivedItems: function () {
            return derived(this._store, ($store) => {
                return $store.content
            })
        }
    };
    
    

    +page.svelte

    <script>
        import { inventory } from "./stores/inventoryStore";
    
        const derivedItems = inventory.getDerivedItems();
    </script>
    
    <pre>
        {JSON.stringify($derivedItems)}
    </pre>