Héritage/opérations partagées et getters à Pinia
P粉662614213
P粉662614213 2023-12-22 13:50:12
0
2
539

J'ai plusieurs magasins Pinia qui sont censés partager un ensemble d'opérations et de getters, mais je ne sais pas trop comment y parvenir efficacement.

Je développe une application qui permet aux utilisateurs de gérer de nombreux médias différents (livres, films, émissions de télévision, etc.). La façon dont j'y pense actuellement est d'avoir un magasin pour chaque type de média, tel que BookStore, MovieStore, etc. De nombreux getters et opérations (comme countdeleteOne) sont exactement les mêmes entre ces différents magasins.

Comment implémenter DRY ici ? Les exemples de la documentation Pinia se concentrent sur la réutilisation d'actions et de getters dans d'autres magasins, mais je ne pense pas que cela réponde pleinement à mon cas d'utilisation consistant à hériter directement d'un ensemble de getters et de setters.

L'approche successorale que j'essaie ici est-elle un anti-modèle ?

P粉662614213
P粉662614213

répondre à tous(2)
P粉420958692

Si vous souhaitez que certaines fonctionnalités ne soient pas partagées dans tous les magasins, vous pouvez utiliser composable.

Vous pouvez créer une fonction composable distincte et y transmettre une partie de l'instance du magasin.

J'ai fait un exemple pour vous sur codesandbox.

Voici un court exemple de codesandbox :

common.ts

import { computed, Ref, ref } from "vue";

export function useCommon(initValue: number) {
    const _value = ref<number>(initValue);

    function increment() {
        _value.value++;
    }

    function someProcessing() {
        // ... some code here
    }

    return {
        counter,

        increment,
        someProcessing,
    };
}

Ensuite, dans n'importe quel magasin, vous pouvez l'utiliser comme ceci :

fooStore.ts

export const useFooStore = defineStore('foo', () => {
    const state = ref<string>('foo');

    const { counter, increment, someProcessing } = useCounter(0);

    return {
        state,

        counter,
        increment,
        someProcessing,
    }
}

De cette façon, vous pouvez composer n'importe quelle fonction, objet, etc. dans n'importe quel stockage ou dans n'importe quel composant.

P粉449281068

Cela peut être réalisé à l'aide de plugins docs

Exemple de film :

Vous possédez plusieurs magasins, chaque état utilisant un schéma de dénomination partagé :

  • Article : élément d'entité unique (détails d'un seul film)
  • collection : collection de projets (collection de tous les films)

Chaque magasin aura les mêmes opérations CRUD, seule l'URL change

  • getCollection : obtenez une liste d'éléments de l'API et définissez la réponse à une collection (https://url.com/movies a>)
  • getItem : obtenez un seul élément de l'API et définissez la réponse à l'élément (https://url.com/movies/ id)
  • handleError : afficher une alerte avec un message d'erreur à l'utilisateur

Créer un plugin :

function BaseStorePlugin () {
    return {
        collection: [],
        item: {},
        getCollection: function (url) {
            api.get(url)
                .then((response) => {
                    this.collection = response.data;
                })
                .catch((error) => {
                    this.handleError(error);
                });
        },
        getItem: function (url) {
            api.get(url)
                .then((response) => {
                    this.item = response.data;
                })
                .catch((error) => {
                    this.handleError(error);
                });
        },
        handleError: function (error) {
            window.alert(error);
        },
    };
}

Fournir des plugins pour Pinia :

const pinia = createPinia();

pinia.use(BaseStorePlugin);

Exemple movieStore.js (utilisant des actions et un état partagés)

import { defineStore } from 'pinia';
import { api } from 'src/boot/axios';

export const useMovieStore = defineStore({
    id: 'movie',
    state: () => ({
        movieSpecificStateObject: {},
    }),
    actions: {
        movieSpecificAction (url) {
            console.log(this.item);
            api.get(url)
                .then((response) => {
                    // handle response
                })
                .catch((error) => {
                    this.handleError(error);
                });
        },
    },
});

Exemples d'utilisation dans les composants

<template>
    <div
        v-for="movie in movieStore.collection"
        :key="movie.id"
    >
        <div>
            {{ movie.name }}
        </div>
    </div>
</template>

<script setup>
import { onMounted } from 'vue';
import { useMovieStore } from 'src/stores/movieStore.js';
const movieStore = useMovieStore();

onMounted(() => {
    movieStore.readCollection('http://url.com/movies');
});
</script>

Editeur : 1

Si vous transmettez le contexte dans le plugin, vous pouvez accéder au magasin et aux options qui y sont transmises, à partir desquelles vous pouvez vérifier l'ID du magasin et renvoyer uniquement le magasin spécifique comme indiqué ci-dessous

function BaseStorePlugin (context) {
  const allowedStores = ['movie', 'album'];

  if (allowedStores.includes(context.store.$id)) {
    return {
      collection: [],
      getCollection: function () {
        const fakeCollection = Array.from({length: 10}, () => Math.floor(Math.random() * 40));
        fakeCollection.forEach((item) => {
          this.collection.push({
            id: item,
            name: `name${item}`
          });
        });
      },
    };
  };
}

J'ai créé un exemple très basique utilisant 3 magasins, le chèque ci-dessus est disponible dans codesandbox ici

Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal