5.5 KiB
Redux in Dashfront
Übersicht, wie Redux Toolkit bisher im Frontend eingesetzt wird.
Stack
- @reduxjs/toolkit — Store, Slices, Async Thunks, Listener Middleware
- react-redux —
Providerinsrc/main.jsx, Anbindung der UI überconnect()(keinuseSelector/useDispatch)
Einstieg:
src/main.jsx → <Provider store={store}>
src/store/index.js → configureStore + Middleware
src/store/*Slice.js → State, Thunks, Selectors
Store-Form
Drei Slices unter src/store/:
| Key | Datei | Verantwortung |
|---|---|---|
auth |
authSlice.js |
Session, Login/Logout, Firmenwahl / -anlage |
company |
companySlice.js |
Aktuelles Unternehmen laden / aktualisieren |
category |
categorySlice.js |
Kategoriebaum, Navigation, Artikel je Kategorie |
// src/store/index.js
configureStore({
reducer: { auth, company, category },
middleware: defaults + listenerMiddleware + (DEV: reduxDevLogger),
});
Typische Muster
Async Thunks als API-Grenze
Seiten- und Dialog-Logik dispatcht Thunks; die Thunks rufen src/api auf und schreiben Ergebnisse in den Slice. Komponenten sprechen die API nicht direkt für diese Domänen an.
Beispiele:
auth/bootstrap,auth/login,auth/selectCompany,auth/createCompany,auth/logoutcompany/loadCurrent,company/updatecategory/loadTree,category/loadArticles,category/create,category/createArticle,category/delete
Fehler in Category-Thunks laufen über catchBackendError / rejectWithValue und landen als BackendError in state.category.error. Tree- und Artikel-Responses werden mit requireObject / requireArray / requireDefined aus apiShape.js validiert.
Selectors
Jeder Slice exportiert select…-Funktionen. UI-Komponenten mappen sie in mapStateToProps:
const mapStateToProps = (state) => ({
status: selectAuthStatus(state),
needsCompanySetup: selectNeedsCompanySetup(state),
});
export default connect(mapStateToProps)(App);
Abgeleitete Category-Selectors (Breadcrumbs, Kinder, „kann zurück“) lesen die Map categories und currentCategoryId — keine zweite Kopie des Baums in der UI.
connect() statt Hooks
Die Redux-gebundenen Komponenten sind Class- oder Function-Komponenten mit:
connect(mapStateToProps, mapDispatchToProps)(Component)
Dispatch-Props wrappen Thunks, oft mit .unwrap() in der Komponente für Erfolg/Fehler (Snackbar).
Listener: Auth → Company
Nach erfolgreichem Bootstrap, Login, Firmenwahl oder Firmenanlage dispatcht ein Listener automatisch loadCurrentCompany, wenn auth.user.selectedCompanyId gesetzt ist (companyLoadListener in companySlice.js).
Cross-Slice-Reset
logout.fulfilledleertcompanyund setztcategoryaufinitialStateloadCurrentCompany.fulfilledmit neuercompanyIdsetzt den Category-State zurück (Firmenwechsel)
Datenfluss (Kurz)
App-Start
└─ AuthBootstrap → bootstrapSession
└─ (listener) loadCurrentCompany wenn selectedCompanyId
└─ Category-UI → loadCategoryTree (braucht company.rootCategoryId)
Login / Firmenwahl / -anlage
└─ auth-Thunks → user.selectedCompanyId
└─ (listener) loadCurrentCompany → ggf. Category-Reset
Kategorien-Seite
└─ navigateToCategory / navigateBack (sync)
└─ loadCategoryArticles für Unterkategorien
└─ createCategory / createArticle / deleteCategory (optimistisch im Map-/Listen-State)
Status-Felder:
| Slice | Feld(er) | Werte (typisch) |
|---|---|---|
| auth | status |
loading / anon / authed |
| company | status |
idle / loading / succeeded / failed |
| category | treeStatus, articlesStatus |
idle / loading / succeeded / failed |
Auth-Slice
- State:
user,googleClientId,status - Setup-Gate:
selectNeedsCompanySetup→ eingeloggt, aber keineselectedCompanyId→CompanySetupDialog mergeUserbehält beim Partial-Update ggf. die bisherigeselectedCompanyId
Company-Slice
- State:
current(normalisiert übernormalizeCompany),status,error - Thunks lesen
auth.user.selectedCompanyIdaus dem Store (getState) - Selectors liefern u. a. Name,
iso639, Steuersätze,rootCategoryIdfür Category/UI
Category-Slice
- Baum:
categoriesals Id→Node-Map, plusrootCategoryId,rootArticles,languageId - Navigation:
currentCategoryId; Root zeigtrootArticles, Unterkategorien laden perloadCategoryArticles - Sync-Actions:
navigateToCategory,navigateBack,resetCategoryState - Hilfsfunktionen (
normalizeCategoryNode,parseCategoryTreeResponse, Breadcrumbs, Slugs) leben im Slice und werden u. a. voncategoryRoute.jsfür URL-Pfade wiederverwendet
Dev-Logging
In Development loggt reduxDevLogger Actions unter auth/, company/, category/ inkl. Payload und kurzem Auth-/Company-Snapshot nach dem Reduce.
Was (noch) nicht in Redux liegt
Artikel-Detail, Fotos und ähnliche lokale UI-Zustände werden in Komponenten / lokalen Props gehalten, nicht als eigener Slice. Neue Domänen folgen dem bestehenden Muster: API in src/api, Thunk + Validierung im Slice, Selectors + connect in der UI.