Nuxt.js 3 : Gestion des États avec Pinia

Introduction

Dans les applications modernes, la gestion d’état devient essentielle pour maintenir la cohérence des données et faciliter la communication entre différents composants. C’est là que Pinia entre en jeu : en tant que store officiel de Vue.js, il est conçu pour offrir une alternative plus simple et intuitive à Vuex. Pinia permet de centraliser facilement les états de votre application, ce qui facilite le partage de données entre composants et simplifie les mises à jour en temps réel. Dans un projet Nuxt.js, Pinia est particulièrement utile pour gérer des états globaux, comme les informations utilisateurs, les paramètres de configuration, et bien plus encore. Dans cet article, nous allons explorer comment installer, configurer et utiliser Pinia pour une gestion d’état optimale dans un projet Nuxt.js.

Prérequis

Avant de commencer, il est essentiel d’avoir un projet Nuxt.js déjà initialisé. Si ce n’est pas encore fait, vous pouvez suivre notre article sur l’initialisation d’un projet Nuxt.js 3 optimisé avec Bootstrap, qui vous guidera à travers les étapes de configuration de base.

Use Case

Nous allons mettre en place un store avec Pinia pour gérer les données utilisateurs de manière globale. Cet exemple se concentrera sur la gestion des informations de profil, illustrant ainsi la centralisation des données et les avantages d’une gestion d’état optimisée. Dans une application Nuxt.js, un store global facilite le partage des informations entre les différents composants, permettant un accès et une mise à jour fluide des données utilisateur telles que le nom, l’email ou encore l’image de profil.
Grâce à cette configuration centralisée, les composants accèdent aux données de manière uniforme sans besoin de passer par des props ou des événements complexes. De plus, toute modification dans un composant se reflète instantanément dans l’ensemble de l’application, assurant ainsi une expérience utilisateur cohérente et des données toujours à jour.

Installation de Pinia

Pour installer Pinia et l’intégrer comme plugin global dans votre projet, ouvrez un terminal dans le répertoire racine de votre application et exécutez la commande suivante :

yarn add pinia @pinia/nuxt

Configuration de Pinia

Nuxt.js facilite l’intégration de Pinia en tant que module. Une fois Pinia installé, vous n’avez pas besoin de créer un plugin dédié. Il vous suffit de déclarer Pinia dans le fichier nuxt.config.ts, où il sera chargé automatiquement dans tous vos composants.

// nuxt.config.js
export default defineNuxtConfig({
  // ... other options
  modules: [
    // ...
    '@pinia/nuxt',
  ],
})

Avec cette étape, Pinia est maintenant complètement intégré dans votre projet Nuxt.js et prêt à être utilisé dans vos composants pour gérer les états globaux.

Création d’un store

Pour illustrer l’utilisation de Pinia, créons un store nommé userStore pour centraliser les données de profil utilisateur.

Commencez par créer un dossier stores à la racine de votre projet, puis un fichier user.js dans ce dossier. Voici un exemple de store :

import { defineStore } from 'pinia';
import { ref } from 'vue';

export const useUserStore = defineStore('user', () => {
  const userData = ref({
    firstName: 'John',
    lastName: 'Doe',
    email: '[email protected]',
    phoneNumber: '+012 (345) 6789',
    address: '205 Main Street, USA'
  });

  function setUserData(newUserData) {
    userData.value = { ...userData.value, ...newUserData };
  }

  return { userData, setUserData };
});

Dans cet exemple, useUserStore contient un état avec les données utilisateur (firstName, lastName, email, phoneNumber, address) et une action setUserData pour mettre à jour ces informations.

Utilisation du store

Une fois le store défini, vous pouvez l’utiliser dans vos composants pour accéder aux données utilisateur ou les modifier.

Par exemple, dans un composant de profil, vous pouvez accéder aux informations du store useUserStore :

<template>
    <div class="col-xl-4">
      <div class="card mb-4 mb-xl-0">
        <div class="card-header">Profile Picture</div>
        <div class="card border-0 shadow">
           <div class="card-body text-center"></div>
            <img class="border img-account-profile d-block mx-auto rounded-circle mb-2" src="@/assets/image/profile01.jpeg" height="50%" width="50%" alt="">
            <div class="card-body p-1-9 p-xl-5">
                <div class="mb-4">
                    <h3 class="h4 mb-0">{{ userData.firstName }} {{ userData.lastName }}</h3>
                </div>
                <ul class="list-unstyled mb-4">
                    <li class="mb-3"><a href="#!"><i class="far fa-envelope display-25 me-3 "></i>{{ userData.email }}</a></li>
                    <li class="mb-3"><a href="#!"><i class="fas fa-mobile-alt display-25 me-3 "></i>+012 (345) 6789</a></li>
                    <li><a href="#!"><i class="fas fa-map-marker-alt display-25 me-3 "></i>205 Main Street, USA</a></li>
                </ul>
                <ul class="social-icon-style2 ps-0">
                    <li><a href="#!" class="rounded-3"><i class="fab fa-facebook-f"></i></a></li>
                    <li><a href="#!" class="rounded-3"><i class="fab fa-twitter"></i></a></li>
                    <li><a href="#!" class="rounded-3"><i class="fab fa-youtube"></i></a></li>
                    <li><a href="#!" class="rounded-3"><i class="fab fa-linkedin-in"></i></a></li>
                </ul>
            </div>
        </div>
        </div>
    </div>
  </template>

<script setup>
import { storeToRefs } from 'pinia';
import { useUserStore } from '@/stores/user';

const userStore = useUserStore();
const { userData } = storeToRefs(userStore);
</script>

<style>

.card {
    box-shadow: 0 0.15rem 1.75rem 0 rgb(33 40 50 / 15%);
}
.card .card-header {
    font-weight: 500;
}
.card-header:first-child {
    border-radius: 0.35rem 0.35rem 0 0;
}
.card-header {
    padding: 1rem 1.35rem;
    margin-bottom: 0;
    background-color: rgba(33, 40, 50, 0.03);
    border-bottom: 1px solid rgba(33, 40, 50, 0.125);
}
.social-icon-style2 li a {
    display: inline-block;
    font-size: 14px;
    text-align: center;
    color: #ffffff;
    background: #3f50e9;
    height: 41px;
    line-height: 41px;
    width: 41px;
}
.rounded-3 {
    border-radius: 0.3rem !important;
}

.social-icon-style2 {
    margin-bottom: 0;
    display: inline-block;
    padding-left: 10px;
    list-style: none;
}

.social-icon-style2 li {
    vertical-align: middle;
    display: inline-block;
    margin-right: 5px;
}

a, a:active, a:focus {
    color: #616161;
    text-decoration: none;
    transition-timing-function: ease-in-out;
    -ms-transition-timing-function: ease-in-out;
    -moz-transition-timing-function: ease-in-out;
    -webkit-transition-timing-function: ease-in-out;
    -o-transition-timing-function: ease-in-out;
    transition-duration: .2s;
    -ms-transition-duration: .2s;
    -moz-transition-duration: .2s;
    -webkit-transition-duration: .2s;
    -o-transition-duration: .2s;
}

.text-secondary, .text-secondary-hover:hover {
    color: #59b73f !important;
}
.display-25 {
    font-size: 1.4rem;
}

.text-primary, .text-primary-hover:hover {
    color: #ff712a !important;
}

p {
    margin: 0 0 20px;
}

.mb-1-6, .my-1-6 {
    margin-bottom: 1.6rem;
}
</style>

Ici, nous affichons les informations utilisateur en récupérant les données depuis userData dans le store userStore.

Pour mettre à jour les informations du store, vous pouvez appeler l’action setUserData depuis un composant ou une fonction :

<template>
    <div class="col-xl-8">
        <!-- Account details card-->
        <div class="card mb-4">
            <div class="card-header">Account Details</div>
            <div class="card-body">
                <form>
                    <!-- Form Row-->
                    <div class="row gx-3 mb-3">
                        <!-- Form Group (first name)-->
                        <div class="col-md-6">
                            <label class="small mb-1" for="inputFirstName">First name</label>
                            <input v-model="newUserData.firstName" class="form-control" id="inputFirstName" type="text" placeholder="Enter your first name">
                        </div>
                        <!-- Form Group (last name)-->
                        <div class="col-md-6">
                            <label class="small mb-1" for="inputLastName">Last name</label>
                            <input v-model="newUserData.lastName" class="form-control" id="inputLastName" type="text" placeholder="Enter your last name">
                        </div>
                    </div>
                    <!-- Form Group (email address)-->
                    <div class="col-md-6">
                        <label class="small mb-1" for="inputEmailAddress">Email address</label>
                        <input v-model="newUserData.email" class="form-control" id="inputEmailAddress" type="email" placeholder="Enter your email address">
                    </div>
                    <!-- Form Row-->
                    <div class="row gx-3 mb-3">
                        <!-- Form Group (phone number)-->
                        <div class="col-md-6">
                            <label class="small mb-1" for="inputPhone">Phone number</label>
                            <input v-model="newUserData.phoneNumber" class="form-control" id="inputPhone" type="tel" placeholder="Enter your phone number">
                        </div>
                        <!-- Form Group (address)-->
                        <div class="col-md-6">
                            <label class="small mb-1" for="inputAddress">Address</label>
                            <input v-model="newUserData.address" class="form-control" id="inputAddress" type="text" name="address" placeholder="Enter your address">
                        </div>
                    </div>
                    <!-- Save changes button-->
                    <button class="btn btn-primary" type="button" @click="updateProfile">Save changes</button>
                </form>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref } from 'vue';
import { useUserStore } from '@/stores/user';

const userStore = useUserStore();
const newUserData = ref({
    firstName: userStore.userData.firstName,
    lastName: userStore.userData.lastName,
    email: userStore.userData.email,
    phoneNumber: userStore.userData.phoneNumber,
    address: userStore.userData.address,
    profileImage: ''
});

function updateProfile() {
    userStore.setUserData(newUserData.value);
}
</script>

<style>
.card {
    box-shadow: 0 0.15rem 1.75rem 0 rgb(33 40 50 / 15%);
}
.card .card-header {
    font-weight: 500;
}
.card-header:first-child {
    border-radius: 0.35rem 0.35rem 0 0;
}
.card-header {
    padding: 1rem 1.35rem;
    margin-bottom: 0;
    background-color: rgba(33, 40, 50, 0.03);
    border-bottom: 1px solid rgba(33, 40, 50, 0.125);
}
.form-control, .dataTable-input {
    display: block;
    width: 100%;
    padding: 0.875rem 1.125rem;
    font-size: 0.875rem;
    font-weight: 400;
    line-height: 1;
    color: #69707a;
    background-color: #fff;
    background-clip: padding-box;
    border: 1px solid #c5ccd6;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    border-radius: 0.35rem;
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
</style>

Dans EditUserProfile, les informations utilisateur peuvent être modifiées en utilisant setUserData pour mettre à jour les données stockées dans userStore.

Test

Pour tester l’intégration de Pinia dans la gestion des informations utilisateur, créez une page user-account affichant deux composants côte à côte : le composant de gauche, UserProfile, montrera les informations utilisateur actuelles, tandis que le composant de droite, EditUserProfile, permettra de les modifier.

<template>
    <div class="container-xl px-4 mt-4"> 
        <div class="row "> 
            <UserProfile/>
            <EditUserProfile/>
          </div>
    </div>
</template>
  
<script setup>

</script>
  
<style>
body{
  margin-top:20px;
  background-color:#f2f6fc;
  color:#69707a;
}
</style>
  

Mettez à jour le prénom et le nom dans EditUserProfile, et observez comment ces modifications apparaissent instantanément dans UserProfile ainsi que dans la barre de navigation, où le store actualise automatiquement les informations de l’utilisateur. Grâce à cette centralisation des données, toute l’application reste parfaitement synchronisée, offrant une expérience utilisateur fluide et cohérente, comme le montre la vidéo ci-dessous.

Conclusion

L’intégration de Pinia dans un projet Nuxt.js simplifie la gestion des états globaux, améliorant ainsi l’organisation et la maintenabilité de votre code. Avec Pinia, vous pouvez facilement centraliser et partager les informations entre vos composants, permettant ainsi une synchronisation en temps réel des données et une expérience utilisateur plus fluide.

Développeuse Full-Stack à Applize * [email protected] * Plus de publications

Développeuse Full-Stack avec une spécialisation dans les technologies web et une solide compréhension des enjeux climatiques. Diplômée de l'université Joseph Ki-Zerbo, Ouagadougou, Burkina en Informatique et Changement Climatique. Passionné par la création de solutions innovantes pour lutter contre le changement climatique. Compétences avancées en programmation et en analyse de données, associées à une expérience approfondie dans la modélisation climatique et la recherche scientifique.

0 0 votes
Évaluation de l'article
guest
0 Commentaires
Le plus ancien
Le plus récent Le plus populaire
Commentaires en ligne
Afficher tous les commentaires

Ingénierie informatique (SSII)

Applize crée des logiciels métiers pour accompagner les entreprises dans la transition vers le zéro papier.


Avez-vous un projet en tête ? Discutons-en.