mrcrowl / vuex-typex

Typescript builder for strongly-typed access to Vuex Store modules
MIT License
193 stars 22 forks source link

How to properly provide initial state for modules #17

Closed KCMertens closed 6 years ago

KCMertens commented 6 years ago

When creating a module with getModuleBuilder().module('namespace') I can provide initial state to the module, however, when providing the store root with initial state by calling using builder.vuexModule({ state: initialRootState }) do I also have to provide the initial module state here? Are there differences in providing the state through the module itself or through the root? And which one "wins" if I provide both but with different values?

mrcrowl commented 6 years ago

Good question! :smiley:

The ability to pass state to the builder.vuexStore(...) is a side-effect of me using vuex's StoreOptions type as an optional argument to the vuexStore method. This was intended not for setting the state, but to give access to some of the other less-used StoreOptions: plugins, strict.

tbh, I'm not sure what happens if you provide both. I would just set the initialState on the modules themselves.

KCMertens commented 6 years ago

Hmm.. I see.

It might be be a good idea to disallow passing initial state to the store root by changing the type of StoreOptions to one without the state key.
Something like this should do the trick (requires typescript >= 2.8 however):

type StoreOptionsWithoutState = Pick<StoreOptions<RootState>, Exclude<keyof StoreOptions<RootState>, 'state'>>;

A downside I see (and ran in to in my own code) is that it would now no longer be possible to initialize fields managed directly by the root store (and not by a module). If I have a store shape like the following I could initialize the submodule when I first create it, but I can then no longer initialize rootProperty.

type MyRootState = {
    rootProperty: string;
    submodule: {
        submoduleProperty: string;
    }
}

In my own project I got around this by typing my initial root state so that all entries/keys belonging to submodules are removed, leaving only the actual root properties:

// state slice managed by top-level modules
type ModuleRootStates = {
    subModule1Namespace: SubModule1.ModuleRootState;
    subModule2Namespace: SubModule2.ModuleRootState;
};

// state slice managed directly by the store root
type OwnRootState = {
    rootProperty1: string;
    rootProperty2: string;
};

// the actual rootstate 
export type RootState = ModuleRootState&OwnRootState;

Then I have the modules initialize themselves when creating them with .module('namespace', initialModuleRootState), and have the root initialize the remaining properties in .vuexStore({ state: initialOwnRootState}) (though it does require casting ownRootState to any). This seems to work, but it's brittle, because it requires me to repeat the namespaces of all top-level modules both when creating the modules and in the state types.

Maybe I'm just being too particular about my state shape though and mixing modules with root-level properties is a bad idea.

Great little library by the way! 👍