forum icon indicating copy to clipboard operation
forum copied to clipboard

Criando uma Store mais simples com Vue.observable/Vue.defineReactive

Open IgorHalfeld opened this issue 5 years ago • 1 comments

Abri essa thread no twitter falando sobre como usar um state management mais simples no Vue.js em casos de projetos mais simples, usando o Vue.observable(Vue +2.6.6) ou o Vue.defineReactive em situações de versão inferior ao Vue 2.6

A implementação fica assim:

store.js

function createStore({ state, mutations }) {
  return {
    state: Vue.observable(state),
    commit(key, ...args) {
      mutations[key](state, ...args)
    }
  }
}
const store = createStore({
  state: { list: [] },
  mutations: {
    populateList (state) {
      state.list = ['Igor', 'Halfeld']
    }
  }
})

component.vue

<template>
  <ul>
    <li v-for="(item, index) in list" :key="index">{{ item }}</li>
  <ul>
</template>

<script>
import Store from './store'

export default {
  data: () => ({
    list: Store.state.list,
  })
  mounted() {
    Store.commit('populateList')
  }
}
</script>

Muita gente se perguntou o por quê de usar dessa forma, pois parece que estou reinventando a roda, na real estou sim, mas nesse caso é pra ter toda praticidade de ter um state management e ter também um bundle menor, já que eu não preciso de features do Vuex como o watch, subscribeMutation, subscribeAction, etc.. em projetos simples.

IgorHalfeld avatar May 20 '19 16:05 IgorHalfeld

Versão com TS e Vue 3

src/store/index.ts

/* eslint-disable @typescript-eslint/no-explicit-any */
import { reactive } from 'vue'

type StoreMutation<StoreState> = (state: StoreState, ...args: any[]) => void

type StoreMutations<StoreState> = Record<string, StoreMutation<StoreState>>

type Store<StoreState> = {
  state: StoreState
  commit (mutation: string, ...args: any[]): void
}

/**
 * Helper to create dynamic stores
 * Useful to reduce boilerplate.. simple and functional
 * Besides the store is standalone and works fine in a lot of places
 * @param {StoreState} states
 * @param {Record<string, StoreMutation<StoreState>>} mutations
 */
export function createStore<StoreState extends object> (states: StoreState, mutations: StoreMutations<StoreState>): Store<StoreState> {
  return {
    state: reactive<StoreState>(states) as StoreState,
    commit (mutation: string, ...args: any[]) {
      const handler = mutations[mutation]
      if (!handler) {
        return
      }
      handler(states, ...args)
    }
  }
}

Exemplo de store

src/app/store/shopping-cart/index.ts

import { createStore } from 'src/store'
import { Product } from 'src/app/definitions'

type ShoppingCart = {
  products: Product[]
}
const states: ShoppingCart = {
  products: []
}
const mutations = {
  setProducts: (state: ShoppingCart, products: Product[]): void => {
    state.products = products
  }
}
const store = createStore<ShoppingCart>(states, mutations)

export default store

src/views/ShoppingCart.vue

<template>
  <ul>
    <li v-for="(item, index) in list" :key="index">{{ item }}</li>
  <ul>
</template>

<script>
import store from 'src/app/store/shopping-cart'

export default {
  setup () (
    return { list: store.state.products }
  ),
  mounted() {
    store.commit('setProducts', ['Nice'])
  }
}
</script>

wilcorrea avatar Jun 04 '21 21:06 wilcorrea