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