GithubHelp home page GithubHelp logo

rettend / pinia-generic Goto Github PK

View Code? Open in Web Editor NEW
4.0 1.0 1.0 370 KB

Split stores and create Generic stores in Pinia

Home Page: https://rettend.github.io/pinia-generic/

License: MIT License

TypeScript 94.51% HTML 1.54% Vue 3.68% JavaScript 0.27%
generic pinia split vue

pinia-generic's Introduction

pinia-generic Pinia

Pinia Generic Store

npm

  • ๐Ÿงฉ Create generic stores that can be reused, massively reducing duplicate code in large projects
  • ๐Ÿ“ Split stores into multiple files, so relevant code can be grouped together
  • ๐Ÿ“ฆ Use store inheritance to create a hierarchy of generic stores

Installation

npm i pinia-generic

Usage

See the Guide for a detailed walkthrough.

Generic Store

When your project has multiple stores that share a lot of common logic, you can create a generic store that can be reused by all of them.

Use defineGenericStore() to create a generic store.

import { type PiniaStore, defineGenericStore, useStore } from 'pinia-generic'

type BaseStore<T> = PiniaStore<
  'base',
  {
    current: T | null
    all: T[]
  },
  {
    getName: () => string | undefined
  },
  {
    add: (item: T) => void
  }
>

interface BaseType {
  name: string
}

function baseStore<T extends BaseType>() {
  return defineGenericStore<BaseStore<T>>({
    state: {
      current: null,
      all: [],
    },
    getters: {
      getName() {
        return this.current?.name
      },
    },
    actions: {
      add(item: T) {
        this.all.push(item)
      },
    },
  })
}

We have two stores (Catergory and Product) that extend the generic store.

Use useStore() to create a store that extends the generic store.

interface Category extends BaseType {
  id: number
  name: string
}

type CategoryStore = PiniaStore<
  'category',
  {
    description: string
  },
  {
    getMaxId: () => number
  },
  object,
  BaseStore<Category>
>

const useCategoryStore = useStore<CategoryStore, BaseStore<Category>>(
  'category',
  {
    state: {
      description: 'This is a category',
    },
    getters: {
      getMaxId() {
        return this.all.reduce((max, item) => Math.max(max, item.id), 0)
      },
    },
  },
  baseStore<Category>(),
)

interface Product extends BaseType {
  id: number
  name: string
  price: number
}

type ProductStore = PiniaStore<
  'product',
  object,
  {
    getTotal: () => number
  },
  {
    remove: (id: number) => void
  },
  BaseStore<Product>
>

const useProductStore = useStore<ProductStore, BaseStore<Product>>(
  'product',
  {
    state: {
      all: [{ id: 1, name: 'Laptop', price: 50 }],
    },
    getters: {
      getTotal() {
        return this.all.reduce((total, item) => total + item.price, 0)
      },
    },
    actions: {
      remove(id: number) {
        this.all = this.all.filter(item => item.id !== id)
      },
    },
  },
  baseStore<Product>(),
)

We only worked on top of Pinia, because useStore() uses Pinia's defineStore() these can be used like regular Pinia stores.

Both stores will have all the generic store's state, getters and actions.

const product = useProductStore()
const category = useCategoryStore()

product.add({
  id: product.getMaxId() + 1,
  name: 'Phone',
  price: 40,
})

product.getTotal() // 90

Splitting Stores

Stores can be split into multiple files using createState(), createGetters() and createActions().

First we need a type for the store. This will be used to type the this parameter in the getters and actions.

// types.ts
import type { PiniaStore } from 'pinia-generic'

type CategoryStore = PiniaStore<
  'category',
  {
    id: number
    name: string
  },
  {
    getId: () => number
    getName: () => string
  }
>

We have the state, getters and actions in separate files.

// state.ts
import { createState } from 'pinia-generic'
import type { CategoryStore } from './types'

const state = createState<CategoryStore>({
  id: 0,
  name: 'Category',
})
// getters.ts
import { createGetters } from 'pinia-generic'
import type { CategoryStore } from './types'

const getters = createGetters<CategoryStore>({
  getId() {
    return this.id
  },
  getName() {
    return this.name
  },
})

And finally we create the store.

// store.ts
import { defineStore } from 'pinia'
import { state } from './state'
import { getters } from './getters'

export const useCategoryStore = defineStore('category', {
  state: () => state,
  getters,
})

Note that there was nothing generic here, so we can use Pinia's defineStore() instead of useStore().

License

MIT

pinia-generic's People

Contributors

dependabot[bot] avatar rettend avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

anikaymaasquesa

pinia-generic's Issues

Example of multi-depth inheritence

So I have successfully used pinia-generic in my project to extend a base store. An example would be for the generic:
https://github.com/stamp-web/stamp-web-vuejs/blob/main/src/stores/baseModelStore.ts

and then the implementaton:
https://github.com/stamp-web/stamp-web-vuejs/blob/main/src/stores/albumStore.ts

This works Perfectly! and compared to my first attempt of a single store having entity type named methods is a thing of beauty. But I have been having trouble figuring out how to declare a level in between. ie. a Generic Store to extend a Generic Store. Ideally I'd want something like

baseModelStore<T extends PersistedModel> { }
baseManagedStore<T extends PersistedNamedModel>{ // extends the baseModelStore but has some new implementation }
albumStore extends baseManagedStore<Album>

For the baseManagedStore I created my type that extended the BaseModelStoreType but then it was unclear whether for the store I have to make this "useStore" or use the "defineGenericStore". The later seems like what I'd want here - to define a generic, add some implementation but extend the baseModelStore - just not sure how to make it "extend" the baseModelStore I guess.

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.