Files
dashfront/README.redux.md
2026-08-01 03:37:40 +02:00

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 — Provider in src/main.jsx, Anbindung der UI über connect() (kein useSelector / 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/logout
  • company/loadCurrent, company/update
  • category/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.fulfilled leert company und setzt category auf initialState
  • loadCurrentCompany.fulfilled mit neuer companyId setzt 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 keine selectedCompanyId → CompanySetupDialog
  • mergeUser behält beim Partial-Update ggf. die bisherige selectedCompanyId

Company-Slice

  • State: current (normalisiert über normalizeCompany), status, error
  • Thunks lesen auth.user.selectedCompanyId aus dem Store (getState)
  • Selectors liefern u. a. Name, iso639, Steuersätze, rootCategoryId für Category/UI

Category-Slice

  • Baum: categories als Id→Node-Map, plus rootCategoryId, rootArticles, languageId
  • Navigation: currentCategoryId; Root zeigt rootArticles, Unterkategorien laden per loadCategoryArticles
  • Sync-Actions: navigateToCategory, navigateBack, resetCategoryState
  • Hilfsfunktionen (normalizeCategoryNode, parseCategoryTreeResponse, Breadcrumbs, Slugs) leben im Slice und werden u. a. von categoryRoute.js fü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.