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

69 views Asked by At

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();

1

There are 1 answers

0
Icaruk On BEST ANSWER

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 slow
            };
            _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>