u
This commit is contained in:
134
README.redux.md
Normal file
134
README.redux.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# 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 |
|
||||
|
||||
```js
|
||||
// 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`:
|
||||
|
||||
```js
|
||||
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:
|
||||
|
||||
```js
|
||||
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.
|
||||
Reference in New Issue
Block a user