• Stars
    star
    62
  • Rank 490,914 (Top 10 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 4 years ago
  • Updated about 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

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 ๐Ÿ˜ƒ

More Repositories

1

vite_ruby

โšก๏ธ Vite.js in Ruby, bringing joy to your JavaScript experience
Ruby
1,270
star
2

iles

๐Ÿ The joyful site generator
TypeScript
1,074
star
3

vite-plugin-image-presets

๐Ÿ–ผ Image Presets for Vite.js apps
TypeScript
246
star
4

vite-plugin-environment

Easily expose environment variables in Vite.js
TypeScript
144
star
5

vite-plugin-full-reload

โ™ป๏ธ Automatically reload the page when files are modified
JavaScript
134
star
6

oj_serializers

โšก๏ธ Faster JSON serialization for Ruby on Rails. Easily migrate away from Active Model Serializers.
Ruby
113
star
7

js_from_routes

๐Ÿ›ฃ๏ธ Generate path helpers and API methods from your Rails routes
Ruby
98
star
8

types_from_serializers

โœ… Generate TypeScript interfaces from your JSON serializers
Ruby
92
star
9

request_store_rails

๐Ÿ“ฆ Per-request global storage for Rails prepared for multi-threaded apps
Ruby
85
star
10

vue-custom-element-example

An example on how to define custom elements using Vue 3
TypeScript
59
star
11

jekyll-vite

โšก๏ธ๐Ÿฉธ Use Vite.js in Jekyll as your assets pipeline
Ruby
50
star
12

vite-plugin-stimulus-hmr

โšก๏ธ HMR for Stimulus controllers in Vite.js
TypeScript
50
star
13

mongoid_includes

๐ŸŒฟ Improves eager loading support for Mongoid
Ruby
46
star
14

stimulus-vite-helpers

Helpers to easily load all your Stimulus controllers when using Vite.js
TypeScript
43
star
15

queryable

โ” Gives your queries a home and avoid tucking scopes inside your models
Ruby
42
star
16

capybara-compose

โœ… Easily write fluent integration tests with Capybara in Ruby
Ruby
32
star
17

better_settings

โš™ Settings for Ruby apps โ€“ fast, immutable, better.
Ruby
20
star
18

vite-plugin-bugsnag

Report builds and upload source maps to Bugsnag
TypeScript
18
star
19

i18n_multitenant

๐ŸŒŽ Provides a convenient way to use tenant-specific translations
Ruby
18
star
20

vite-plugin-manifest-sri

Subresource Integrity for Vite.js manifest files
JavaScript
15
star
21

resourcerer

โœจ Works like magic to dry up your controllers
Ruby
10
star
22

sublime-toggle-dark-mode

๐ŸŒš๐ŸŒž Toggle between dark and light mode in Sublime Text 4
JavaScript
10
star
23

pakiderm

๐Ÿ˜ Pakiderm will never forget the return value
Ruby
7
star
24

presenter_rails

๐Ÿ”ญ Expose your view models in a convenient way
Ruby
6
star
25

vite-plugin-erb

Use ERB files in Vite.js projects with a Ruby backend
TypeScript
5
star
26

journeyman

Let your factories use your business logic, making them flexible and easier to update.
Ruby
5
star
27

jekyll-vite-minima

โšก๏ธ๐Ÿฉธ Use Vite.js in Jekyll minima theme as your assets pipeline
Ruby
3
star
28

automatic-music-transcription

Automatically exported from code.google.com
C
2
star
29

vite-plugin-xdm

Use XDM in VIte.js
JavaScript
2
star
30

fast-food-mvc

Automatically exported from code.google.com
C#
1
star
31

ElMassimo

1
star
32

vite-vue-router-hmr-repro

Vue
1
star
33

crouton

๐Ÿž Context sensitive notifications for Rails
Ruby
1
star