Skip to main content

State Management

Overview

Smart Academic Hub uses Pinia for centralized state management.

Store Structure

Stores are organized by domain:

stores/
├── auth.ts # Authentication state
├── academy.ts # Academy context
├── students.ts # Student data
├── courses.ts # Course data
└── ui.ts # UI state (modals, notifications)

Creating a Store

import { defineStore } from 'pinia';

export const useStudentStore = defineStore('students', {
state: () => ({
students: [] as Student[],
loading: false,
error: null as string | null,
}),

getters: {
activeStudents: (state) =>
state.students.filter(s => s.status === 'active'),
},

actions: {
async fetchStudents() {
this.loading = true;
try {
const response = await studentService.getAll();
this.students = response.data;
} catch (error) {
this.error = error.message;
} finally {
this.loading = false;
}
},
},
});

Using Stores in Components

<script setup lang="ts">
import { useStudentStore } from '@/stores/students';

const studentStore = useStudentStore();

// Access state
const students = computed(() => studentStore.students);

// Call actions
onMounted(() => {
studentStore.fetchStudents();
});
</script>

Store Composition

Stores can use other stores:

export const useEnrollmentStore = defineStore('enrollments', () => {
const studentStore = useStudentStore();
const courseStore = useCourseStore();

// ... store logic
});

Persisting State

Use pinia-plugin-persistedstate for localStorage:

import { defineStore } from 'pinia';

export const useAuthStore = defineStore('auth', {
state: () => ({
token: null,
user: null,
}),
persist: true, // Automatically persists to localStorage
});

Best Practices

  1. Keep stores focused on single domains
  2. Use getters for computed state
  3. Handle loading and error states
  4. Don't mutate state directly outside actions
  5. Use TypeScript for type safety
  6. Persist only necessary state
  7. Reset state on logout