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
- Keep stores focused on single domains
- Use getters for computed state
- Handle loading and error states
- Don't mutate state directly outside actions
- Use TypeScript for type safety
- Persist only necessary state
- Reset state on logout