Giter Site home page Giter Site logo

elmassimo / vuex-stores Goto Github PK

View Code? Open in Web Editor NEW
63.0 3.0 2.0 1.32 MB

๐Ÿ—„ Store objects for Vuex, a simple and more fluid API for state-management.

Home Page: https://maximomussini.com/posts/vuex-stores

License: MIT License

JavaScript 94.33% Vue 5.67%
vuex vuex2 vuex3 vuex-store vuex-plugin vuex-modules vue maintainability js

vuex-stores's Introduction

Prefer pinia if using Vue 3 or @vue/composition-api in Vue 2

Vuex Stores ๐Ÿ—„

Gem Version Build Status Code Climate License

Store objects for Vuex 3, a simple and more fluid API for state-management.

Why? ๐Ÿค”

Dispatching actions and injecting getters in Vuex requires using String namespaces and action names, which is verbose and makes it hard to detect typos.

Injecting state, getters, and actions using the map helpers is sometimes cumbersome, and is only suitable for components.

Store objects address these issues by allowing access to state and getters as properties, and dispatching actions easily by using plain method calls.

Read more about the benefits in the blog announcement.

Installation โš™๏ธ

npm install --save vuex-stores

or if using yarn:

yarn add vuex-stores

API โŒจ๏ธ

registerAndGetStore allows to dynamically register a module in the specified Vuex store, returning a Store Object which can be used to easily access state, getters, and actions, abstracting away the namespace for that module.

import { registerAndGetStore } from 'vuex-stores'

const WindowStore = registerAndGetStore(vuexStore, { namespace, state, getters, mutations, actions })

State ๐Ÿ—ƒ

State can be accessed as properties in the store object:

const state = {
  isFullscreen: false,
  windowHeight: 768,
  windowWidth: 1024,
}

// A property is available for every property in the state:

WindowStore.isFullscreen // false
WindowStore.windowHeight // 768
WindowStore.windowWidth // 1024

// instead of

this.$store.state.window.windowWidth // โŒ

Getters โœ‹

Getters can be accessed as properties in the store object:

const getters = {
  windowSize (state) {
    return state.windowHeight * state.windowWidth
  },
}

// A property is available for every getter:

WindowStore.windowSize // 1024 * 768 = 786,432

// instead of

this.$store.getters['window/windowSize'] // โŒ

Actions โšก๏ธ

Actions can be dispatched by calling methods in the store object:

export const actions = {
  setFullscreen ({ commit }, isFullscreen) {
    commit('SET_FULLSCREEN', isFullscreen)
  },
  updateWindowSize ({ commit }, size = { height: window.innerHeight, width: window.innerWidth }) {
    commit('SET_WINDOW_SIZE', size)
  },
}

// A method is available for every action:
WindowStore.setFullscreen(true)
WindowStore.updateWindowSize()
WindowStore.updateWindowSize({ width: 1024, height: 768 })

// instead of
this.$store.dispatch('window/updateWindowSize', { width: 1024, height: 768 }) // โŒ

By convention, mutations should be an internal detail, so they are not exposed.

mapState, mapGetters, mapActions

These usual helpers are available, allowing us to inject properties and methods in a component, without having to deal with the namespace:

computed: {
  ...WindowStore.mapState('windowHeight', 'windowWidth'),
  ...WindowStore.mapGetters('windowSize'),
},
methods: {
  ...WindowStore.mapActions('setFullscreen')
},

These are mostly helpful when the values are used in the template. Else, we have a better option:

methods: {
  onToggleFullscreen (event) {
    WindowStore.setFullscreen(!WindowStore.isFullscreen)
  },
},

An additional benefit is that references to the state and actions are more explicit, and don't require manual boilerplate, making the code easier to understand and refactor ๐Ÿ˜€

watch ๐Ÿ‘

Makes it convenient to watch a store value outside the component lifecycle:

WindowStore.watch('windowSize', windowSize => console.log('onWindowSizeChange', windowSize))

WindowStore.watch('isNavigating',
  isNavigating => isNavigating ? NProgress.start() : NProgress.done(),
  { sync: true }, // Any watcher options can be provided, such as `immediate`.
)

Other less commonly used API properties and methods include:

  • buildStoreObject: Like registerAndGetStore, but doesn't call registerModule.
  • registerModule: Used internally by registerAndGetStore to register the module in Vuex.
  • unregisterModule: Can be used to remove the module from the Vuex store.
  • moduleNamespace: The module name for this store object in the Vuex store, relevant if using a factory pattern.

Recommended Setup ๐Ÿ› 

The recommended setup involves exporting the Vuex.Store:

// @app/store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  strict: process.env.NODE_ENV !== 'production'
})

And creating one file per store module, exporting the store object.

// @stores/ModalStore.js

import VuexStore from '@app/store'
import { registerAndGetStore } from 'vuex-stores'

const namespace = 'modals'
const state = () => ({ ... })
const getters = { ... }
const mutations = { ... }
const actions = { ... }

export default registerAndGetStore(VuexStore, { namespace, state, getters, mutations, actions })

This makes it very convenient to import the store object from a component:

// @components/ModalManager.vue

<script>
import ModalsStore from '@stores/ModalsStore'

export default {
  name: 'ModalManager',
  computed: ModalsStore.mapState('modals'),
  beforeMount () {
    // Hide modals when visiting a different route.
    if (this.$router) this.$router.afterEach(ModalsStore.closeAllModals)
  },
  methods: {
    onModalClose (modal, event) {
      if (!event.defaultPrevented) ModalsStore.removeModal(modal)
    },
  },
}
</script>

<template>
  <span class="modal-manager">
    <component
      :is="modal.component"
      v-for="modal in modals"
      :key="modal.id"
      v-bind="modal.attrs"
      v-on="modal.listeners"
      @modal:close.native="onModalClose(modal, $event)"
    />
  </span>
</template>

Feel free to check the tests for additional usage examples, and setup options.

Dynamic Stores (Factory) ๐Ÿ’ 

What happens if we need more than one instance of a store? Instead of exporting a single store object, we can export a function that dynamically registers a new store object on each invocation. For example:

// @stores/FormStoreFactory
let formId = 0

export default (id = `form-${formId++}`) =>
  registerAndGetStore(store, { namespace: id, state, getters, mutations, actions })

And then import the factory:

import FormStoreFactory from '@stores/FormStoreFactory'

// A new module is registered with a dynamic namespace.
const FormStore = FormStoreFactory()

These dynamic store objects can be passed to child components using provide and inject, or directly as props, and provide all the advantages from Vuex, such as a well defined data-schema for the state, and having the history of changes available in the Vue devtools, making it very convenient for complex hierarchies.

You can call registerModule and unregisterModule on the store object to manage the lifecycle, unregistering them once they are no longer necessary to free up some memory.

Farewell

The patterns described above are just a few of many possibilities.

Nothing prevents you from using a more complex strategy, like creating a store of stores, which has a Map of store objects, and uses actions to register and unregister new store objects โ™ป๏ธ

Let me know if you come up with new or creative ways to use it ๐Ÿ˜ƒ

vuex-stores's People

Contributors

dependabot[bot] avatar elmassimo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

vuex-stores's Issues

Vuex dependecy BC break

I have a Nuxt project which uses your vuex-stores library. Today I made some changes in package.json and find out that there are some BC breaks in the Vuex dependency. It seems it is caused by your version signature which says

  "dependencies": {
    "vuex": ">2.0"
  },

This downloads me the Vuex repository of version: "4.0.2",

This contains the code

  store._state = vue.reactive({
    data: state
  });

in vuex.cjs.js which throws me an error
vue.reactive is not a function

I dont know if I have right but if yes, could you fix it please? Thanks a lot.

Support for Typescript

Hi!

This is a great library and I foresee it will help a lot when writing component with Vue Composition API.

Is there a plan to add the typing interface as well in the script?

Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.