Search code examples
vue.jspinia

Is it possible to optimize this pinia store


I am converting an existing app with a complex vuex store to pinia.

When a user logs in, we get a response with all of their access levels as a json string. There are actually many more access objects but I have truncated them for brevity.

My store works but I am wondering if there is a more efficient way to structure my pinia stores. Specifically the $reset and setStoreFromResponse methods could be less verbose/more efficient

// user-access.js

import { ref } from 'vue'
import { defineStore } from 'pinia'

export const useUserAccessStore = defineStore('user-access', () => {
  const lvl = ref(1) 
  const pln = ref(1) 
  const uce = ref(false) 
  const trl = ref(false) 
  const snl = ref({ b: 1, a: true }) 
  const snd = ref({ b: 3, a: false }) 
  const sms = ref({ b: 3, a: false })
  const ssm = ref({ b: 3, a: false })
  const slm = ref({ b: 4, a: false })
  const fvt = ref({ b: 2, a: false })

  const $reset = () => {
    lvl.value = 1
    pln.value = 1
    uce.value = false
    trl.value = false
    snl.value = { b: 1, a: true }
    snd.value = { b: 3, a: false }
    sms.value = { b: 3, a: false }
    ssm.value = { b: 3, a: false }
    slm.value = { b: 4, a: false }
    fvt.value = { b: 2, a: false }
  }
  return {
    pln, lvl, uce, trl, snl, snd, sms, ssm, slm, fvt,
    $reset }
})
// user-access-set.js

import { computed, ref } from 'vue'
import { useUserAccessStore } from './user-access'
import { storeToRefs, defineStore } from 'pinia'

export const useUserAccessSetStore = defineStore('user-access-set', () => {
  const userAccess = useUserAccessStore()
  const {pln, lvl, uce, trl, snl, snd, sms, ssm, slm, fvt} = storeToRefs(userAccess)

  const access = {
    'pln': pln,
    'lvl': lvl,
    'uce': uce,
    'trl': trl,
    'snl': snl,
    'snd': snd,
    'sms': sms,
    'ssm': ssm,
    'slm': slm,
    'fvt': fvt,
  }
  const setStoreFromResponse = (response) => {
    Object.keys(response).forEach(element => {
      if (!access[element]) return;
      access[element].value = response[element]
    });
  }
  return { setStoreFromResponse }
})
// stores/user-access-set.jest.spec.js
import { setActivePinia, createPinia, storeToRefs } from 'pinia'
import { useUserAccessSetStore } from '../src/stores/user-access-set.js'
import { useUserAccessStore } from '../src/stores/user-access.js'

describe('User Access Methods Store', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
  })

  it('it_sets_store_from_response', () => {
    const userAccess = useUserAccessStore()
    expect(userAccess.uce).toBe(false)
    expect(userAccess.trl).toBe(false)
    expect(userAccess.pln).toBe(1)
    expect(userAccess.lvl).toBe(1)
    expect(userAccess.snl).toStrictEqual({ a: true, b: 1})
    const { setStoreFromResponse } = useUserAccessSetStore()
    const response = {
      uce: true,
      trl: true,
      pln: 4,
      lvl: 4,
      snl: {
        b: 4, a: false
      }
    }
    setStoreFromResponse(response)
    expect(userAccess.uce).toBe(true)
    expect(userAccess.trl).toBe(true)
    expect(userAccess.pln).toBe(4)
    expect(userAccess.lvl).toBe(4)
    expect(userAccess.snl).toStrictEqual({ a: false, b: 4})

  })

})
// response.json
{
  "uce": true,
  "trl": true,
  "pln": 4,
  "lvl": 4,
  "snl": {
    "b": 4,
    "a": false
  },
  "snd": {
    "b": 4,
    "a": true
  },
  "sms": {
    "b": 4,
    "a": true
  },
  "ssm": {
    "b": 4,
    "a": true
  },
  "slm": {
    "b": 4,
    "a": true
  },
  "fvt": {
    "b": 4,
    "a": true
  }
}

Solution

  • You will naturally have less verbose code when using pinia store options instead of setup function. There aren't really many benefits for the latter, and brevity is certainly not one of them. There should be specific requirements that justify the use of refs in Vue, but here the opposite happens. There's no single state object in setup function that requires to explicitly implement $reset, while you get it for free with options due to state factory function.

    As for setStoreFromResponse, this logic belongs to user-access, this is what a store as an entity is for, it doesn't make sense to create a separate store for this, not to mention that a store isn't supposed to be a glorified composable.

    It could be:

    defineStore('user-access', {
      state: () => ({
        lvl: 1,
        ...
      }),
      actions: {
        update(obj) {
          this.$patch(state => {
            for (let key in obj)
               if (key in state)
                 state[key] = obj[key]; 
          });
        }
     }
    });
    

    The test could use expect(userAccess.$state).toEqual(...)instead of multiple assertions.