Redux-Symbiote - Schreibaktionen und Reduzierungen fast schmerzfrei

React-Redux ist eine großartige Sache. Bei korrekter Verwendung ist die Anwendungsarchitektur effizient und die Projektstruktur leicht zu lesen. Aber wie bei jeder Entscheidung gibt es einige Besonderheiten.

Die Beschreibung von Aktionen und Reduzierern ist eines dieser Merkmale. Die klassische Implementierung dieser beiden Entitäten in Code ist eine ziemlich zeitaufwändige Aufgabe.

Der Schmerz der klassischen Implementierung


Ein einfaches Beispiel:

// actionTypes.js //    export const POPUP_OPEN_START = 'POPUP_OPEN_START '; export const POPUP_OPEN_PENDING = 'POPUP_OPEN_PENDING '; export const POPUP_OPEN_SUCCESS = 'POPUP_OPEN_SUCCESS '; export const POPUP_OPEN_FAIL = 'POPUP_OPEN_FAIL'; export const POPUP_CLOSE_START = 'POPUP_CLOSE_START '; export const POPUP_CLOSE_PENDING = 'POPUP_CLOSE_PENDING '; export const POPUP_CLOSE_SUCCESS = 'POPUP_CLOSE_SUCCESS '; export const POPUP_CLOSE_FAIL = 'POPUP_CLOSE_FAIL'; 

 // actions.js //    import { POPUP_OPEN_START, POPUP_OPEN_PENDING, POPUP_OPEN_SUCCESS, POPUP_OPEN_FAIL, POPUP_CLOSE_START, POPUP_CLOSE_PENDING, POPUP_CLOSE_SUCCESS, POPUP_CLOSE_FAIL } from './actionTypes'; export function popupOpenStart(name) { return { type: POPUP_OPEN_START, payload: { name }, } } export function popupOpenPending(name) { return { type: POPUP_OPEN_PENDING, payload: { name }, } } export function popupOpenFail(error) { return { type: POPUP_OPEN_FAIL, payload: { error, }, } } export function popupOpenSuccess(name, data) { return { type: POPUP_OPEN_SUCCESS, payload: { name, data }, } } export function popupCloseStart(name) { return { type: POPUP_CLOSE_START, payload: { name }, } } export function popupClosePending(name) { return { type: POPUP_CLOSE_PENDING, payload: { name }, } } export function popupCloseFail(error) { return { type: POPUP_CLOSE_FAIL, payload: { error, }, } } export function popupCloseSuccess(name) { return { type: POPUP_CLOSE_SUCCESS, payload: { name }, } } 

 // reducers.js //   import { POPUP_OPEN_START, POPUP_OPEN_PENDING, POPUP_OPEN_SUCCESS, POPUP_OPEN_FAIL, POPUP_CLOSE_START, POPUP_CLOSE_PENDING, POPUP_CLOSE_SUCCESS, POPUP_CLOSE_FAIL } from './actionTypes'; const initialState = { opened: [] }; export function popupReducer(state = initialState, action) { switch (action.type) { case POPUP_OPEN_START: case POPUP_OPEN_PENDING: case POPUP_CLOSE_START: case POPUP_CLOSE_PENDING: return { ...state, error: null, loading: true }; case POPUP_OPEN_SUCCESS : return { ...state, loading: false, opened: [ ...(state.opened || []).filter(x => x.name !== action.payload.name), { ...action.payload } ] }; case POPUP_OPEN_FAIL: return { ...state, loading: false, error: action.payload.error }; case POPUP_CLOSE_SUCCESS: return { ...state, loading: false, opened: [ ...state.opened.filter(x => x.name !== name) ] }; case POPUP_CLOSE_FAIL: return { ...state, loading: false, error: action.payload.error }; } return state; } 

Die Ausgabe hat 3 Dateien und mindestens die folgenden Probleme:

  • "Aufblähen" des Codes durch einfaches Hinzufügen einer neuen Aktionskette
  • übermäßiger Import von Aktionskonstanten
  • Lesen von Aktionskonstantennamen (einzeln)

Optimierung


Dieses Beispiel kann mit Redux-Aktionen verbessert werden.

 import { createActions, handleActions, combineActions } from 'redux-actions' export const actions = createActions({ popups: { open: { start: () => ({ loading: true }), pending: () => ({ loading: true }), fail: (error) => ({ loading: false, error }), success: (name, data) => ({ loading: false, name, data }), }, close: { start: () => ({ loading: true }), pending: () => ({ loading: true }), fail: (error) => ({ loading: false, error }), success: (name) => ({ loading: false, name }), }, }, }).popups const initialState = { opened: [] }; export const accountsReducer = handleActions({ [ combineActions( actions.open.start, actions.open.pending, actions.open.success, actions.open.fail, actions.close.start, actions.close.pending, actions.close.success, actions.close.fail ) ]: (state, { payload: { loading } }) => ({ ...state, loading }), [combineActions(actions.open.fail, actions.close.fail)]: (state, { payload: { error } }) => ({ ...state, error }), [actions.open.success]: (state, { payload: { name, data } }) => ({ ...state, error: null, opened: [ ...(state.opened || []).filter(x => x.name !== name), { name, data } ] }), [actions.close.success]: (state, { payload: { name } }) => ({ ...state, error: null, opened: [ ...state.opened.filter(x => x.name !== name) ] }) }, initialState) 

Schon viel besser, aber der Perfektion sind keine Grenzen gesetzt.

Schmerz behandeln


Auf der Suche nach einer besseren Lösung stieß ich auf einen Kommentar von LestaD habr.com/de/post/350850/#comment_10706454 und beschloss, Redux- Symbiote auszuprobieren .
Dadurch konnten unnötige Entitäten entfernt und die Codemenge reduziert werden.

Das obige Beispiel sah folgendermaßen aus:

 // symbiotes/popups.js import { createSymbiote } from 'redux-symbiote'; export const initState = { opened: [] }; export const { actions, reducer } = createSymbiote(initialState, { popups: { open: { start: state => ({ ...state, error: null }), pending: state => ({ ...state }), success: (state, { name, data } = {}) => ({ ...state, opened: [ ...(state.opened || []).filter(x => x.name !== name), { name, data }) ] }), fail: (state, { error } = {}) => ({ ...state, error }) }, close: { start: state => ({ ...state, error: null }), pending: state => ({ ...state }), success: (state, { name } = {}) => ({ ...state, opened: [ ...state.opened.filter(x => x.name !== name) ] }), fail: (state, { error } = {}) => ({ ...state, error }) } } }); 

 //   import { actions } from './symbiotes/popups'; // ... export default connect( mapStateToProps, dispatch => ({ onClick: () => { dispatch(actions.open.start({ name: PopupNames.Info })); } }) )(FooComponent); 

Von den Profis haben wir:

  • alles in einer Datei
  • weniger Code
  • strukturierte Darstellung von Aktionen

Von den Minuspunkten:

  • IDE bietet nicht immer Hinweise
  • Es ist schwierig, im Code nach Aktionen zu suchen
  • Aktion schwer umzubenennen

Trotz der Nachteile wird dieses Modul erfolgreich in unseren Projekten eingesetzt.

Vielen Dank an LestaD für die gute Arbeit.

Source: https://habr.com/ru/post/de442346/


All Articles