Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it okay to use Javascript class objects in Vuex state?

Tags:

vuex

Lets say I have a simple Vuex state to hold a recipe:

const state = {
    recipe: {
        id: '',
        name: "",
        ingredients : [],
    }
};

Is it a good practice to instead create a recipe class holding the same data, and then in the state declare it as an instance of the class?

const state = {
    recipe: new Recipe()
};

If the recipe class has methods, should those methods be proxied via getters/mutations on the vuex store, or is it okay to call the methods directly?

like image 650
corycorycory Avatar asked Nov 25 '18 17:11

corycorycory


People also ask

Is pinia better than Vuex?

Compared to Vuex, Pinia provides a simpler API with less ceremony, offers Composition-API-style APIs, and most importantly, has solid type inference support when used with TypeScript.

Can Vuex store objects?

Summary. vuex-stores provides a simple way to work with Vuex modules by allowing you to define store objects to focus on one module at a time, making it more enjoyable to leverage Vuex.

What is strict mode in Vuex?

Strict mode runs a synchronous deep watcher on the state tree for detecting inappropriate mutations, and it can be quite expensive when you make large amount of mutations to the state. Make sure to turn it off in production to avoid the performance cost.


1 Answers

While I can not give an authoritative answer, I would say yes, you should use classes in the state. I consider this good practice in line with the Separation of Concern design principle. However, you should not have methods on those classes to call directly.

tl;dr;

  • use classes as DTOs or models
  • keep state clean of methods, but you might add some "locally calculated state" if needed.
  • mutate data as much as you need in the mutations, even mutating some "locally calculated state" from above, if needed.
  • if needed, pick specific data with specific getters, possibly having many getters for the same underlying data
  • if needed, implement separate code outside the state, getters and mutations instead of methods directly in those.

Example

In my application, using vuex and typescript, I have written a "api-model" that represents the various parts of my API responses and requests and some as unitTree.ts helper class code that is responsible for the handling of the enterpriseUnitsTree part of the state, which is a local subset of the caseFullData.

Here's how I do it in the state.ts (simplified):

import { ICaseFullDataViewModel, CaseFullDataViewModel, CaseApprovalDto, ICaseApprovalDto } from '@/api-model';
import { IEnterpriseUnitDataObjectTree } from '../code/unitTree';

export interface IEntryState {
    caseValidationMessages: string[];
    caseFullData: ICaseFullDataViewModel;
    enterpriseUnitsTree: Array<IEnterpriseUnitDataObjectTree>;
    caseApproval: ICaseApprovalDto;
}

export const state: IEntryState = {
    caseValidationMessages: null,
    caseFullData: new CaseFullDataViewModel(),
    enterpriseUnitsTree: null,
    caseApproval: new CaseApprovalDto()
};

As for getting data, I would recommend having a complete set of getters for each aspect you need. It keeps the state clean as really to be only a state, not a business/use case in itself. See below some getters for parts of the above data. For example, I am using external code in unitTree.ts to extract a specific item of enterpriseUnitsTree from the store.

Here's my code in getters.ts (simplified):

import { IRootState } from '@/store';
import { GetterTree } from 'vuex';
import { unitTree } from '../code/unitTree';
import { IEntryState } from './state';

export const CASE_ID = 'caseId';
export const CASE_FULL_DATA = 'caseFullData';
export const UNITS_SORTED_AS_TREE = 'unitsSortedAsTree';
export const FIRST_UNIT = 'firstUnit';


export const getters: GetterTree<IEntryState, IRootState> = {
    [CASE_ID]: state => state.caseFullData?.caseId,  
    [CASE_FULL_DATA]: state => state.caseFullData,
    [UNITS_SORTED_AS_TREE]: state => {
        return state.enterpriseUnitsTree;
    },
    [FIRST_UNIT]: state => () => {        
        const indexMap = unitTree.getIndexMap(state.enterpriseUnitsTree);
        const firstUnitIdentifier = indexMap.get(0);        
        return firstUnitIdentifier;
    }
    //.... 
};

In the mutations.ts, I do mutate the the enterpriseUnitsTree, as needed, when receiving a commit the the respective data:

Here's my code in mutations.ts (simplified):

import { MutationTree } from 'vuex';
import {
    ICaseFullDataViewModel    
} from '@/api-viewmodel';
import { IEntryState } from './state';
import { unitTree } from '../code/unitTree';

export const MAPCASEFULLDATA = 'mapCaseFullData';
//....

export const mutations: MutationTree<IEntryState> = {
    [MAPCASEFULLDATA](state, value: ICaseFullDataViewModel) {
        state.caseFullData = value;
        //... some sorting and tree building using code from unitTree
        state.enterpriseUnitsTree = sortedEnterpriseUnits;
    },
    //.......
};
like image 109
Marcel Avatar answered Sep 21 '22 16:09

Marcel