In diesem Beitrag werden wir auf das Schreiben von Aktionen und Reduzieren eingehen. Stellen Sie sich zunächst einen typischen Ablauf vor, in dem wir die folgenden Vorgänge ausführen (außerdem überarbeiten wir alles so, dass unser Code den SOLID-Grundsätzen entspricht).
1. Erstellen Sie eine Datei mit Konstanten (hier speichern wir die Namen der Aktionstypen)
export const REQUEST_DATA_PENDING = "REQUEST_DATA_PENDING"; export const REQUEST_DATA_SUCCESS = "REQUEST_DATA_SUCCESS"; export const REQUEST_DATA_FAILED = "REQUEST_DATA_FAILED"; export const PROFILES_PER_PAGE = "PROFILES_PER_PAGE"; export const CURRENT_PAGE = "CURRENT_PAGE";
2. Erstellen Sie eine Datei, in der wir die Aktionen beschreiben (hier fordern wir Benutzerkonten und Paginierung an). Auch im Beispiel wurde redux-thunk verwendet (weitere werden wir ähnliche Abhängigkeiten ablehnen):
export const requestBigDataAction = () => (dispatch) => { fetchingData(dispatch, BIG_DATA_URL, 50); } export const changeCurrentPAGE = (page) => ({ type: CURRENT_PAGE, payload: page }) function fetchingData(dispatch, url, profilesPerPage) { dispatch({type: REQUEST_DATA_PENDING}); fetch(url) .then((res) => { if(res.status !== 200) { throw new Error (res.status); } else { return res.json(); } }) .then((data) => {dispatch({type: REQUEST_DATA_SUCCESS, payload: data})}) .then(() => dispatch({type: PROFILES_PER_PAGE, payload: profilesPerPage})) .catch((err) => dispatch({type: REQUEST_DATA_FAILED, payload: ` . ${err.message}`})); }
3. Wir schreiben Reduzierer
import { REQUEST_DATA_PENDING, REQUEST_DATA_SUCCESS, REQUEST_DATA_FAILED, PROFILES_PER_PAGE, CURRENT_PAGE } from '../constants/constants'; const initialState = { isPending: false, buffer: [], data: [], error: "", page: 0, profilesPerPage: 0, detailedProfile: {} } export const MainReducer = (state = initialState, action = {}) => { switch(action.type) { case REQUEST_DATA_PENDING: return Object.assign({}, state, {isPending: true}); case REQUEST_DATA_SUCCESS: return Object.assign({}, state, {page : 0, isPending: false, data: action.payload, error: "", detailedProfile: {}, buffer: action.payload}); case REQUEST_DATA_FAILED: return Object.assign({}, initialState, {error: action.payload}); case PROFILES_PER_PAGE: return Object.assign({}, state, {profilesPerPage: action.payload}); case CURRENT_PAGE: return Object.assign({}, state, {page: action.payload}); default: return state; } }
4. Store konfigurieren (Middleware thunkMiddleware verwenden)
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import {createStore, applyMiddleware} from 'redux'; import {Provider} from 'react-redux'; import thunkMiddleware from 'redux-thunk'; import {MainReducer} from './reducers/mainReducer'; const store = createStore(MainReducer, applyMiddleware(thunkMiddleware)); ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root'));
5. Schließen Sie die Komponente an Redux an
const mapDispatchToProps = (dispatch)=>{ return { onRequestBigData: (event) =>{ dispatch(requestBigDataAction()); } } };
Verbinden Sie die Paginierungstasten mit Redux
const mapDispatchToProps = (dispatch)=>{ return { onChangePage: (page) =>{ dispatch(changeCurrentPAGE(page)); } } };
Problem: Unser Reduzierer ist eine große Schaltanweisung. Wenn wir also eine neue Aktion hinzufügen oder ihr Verhalten ändern, müssen wir unseren Reduzierer ändern, was gegen die Prinzipien von SOlid (das Prinzip der alleinigen Verantwortung und das Prinzip der Offenheit / Nähe) verstößt.
Lösung: Polymorphismus hilft uns. Fügen Sie zu jeder Aktion die Methode execute hinzu, die die Aktualisierung anwendet und den aktualisierten Status zurückgibt. Dann wird unser Reduzierer die Form annehmen
export const MainReducer = (state = initialState, action) => { if(typeof action.execute === 'function') return action.execute(state); return state; };
Wenn wir jetzt eine neue Aktion hinzufügen, müssen wir den Reduzierer nicht ändern und es wird kein riesiges Monster.
Geben Sie als nächstes Redux-Thunk auf und schreiben Sie die Aktionen neu
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import {createStore} from 'redux'; import {Provider} from 'react-redux';
gehe zu der verbundenen Komponente, deren Aktion asynchron ist (sie muss leicht korrigiert werden)
const mapDispatchToProps = (dispatch)=>{ return { onRequestBigData: (event) =>{ requestBigDataAction(dispatch); }, } };
Fahren Sie mit den Aktionen selbst fort und fügen Sie ihnen die Methode execute hinzu
const type = 'bla-bla'; const requestDataPending = {execute: state => ({...state, isPending: true}), type}; const requestDataSuccess = payload => ({ execute: function (state) { return ({...state, page : 0, isPending: false, data: payload, error: "", detailedProfile: {}, buffer: payload}) }, type}) const profilesPerPageAction = profilesPerPage => ({ execute: state => ({...state, profilesPerPage: profilesPerPage}), type }); const requestDataFailed = errMsg => state => ({...state, error: ` . ${errMsg}`}); function fetchingData(dispatch, url, profilesPerPage) { dispatch(requestDataPending); fetch(url) .then((res) => { if(res.status !== 200) { throw new Error (res.status); } else { return res.json(); } }) .then((data) => {dispatch(requestDataSuccess(data))}) .then(() => dispatch(profilesPerPageAction(profilesPerPage))) .catch((err) => dispatch(requestDataFailed(err.message))); } export const requestBigDataAction = (dispatch) => { fetchingData(dispatch, BIG_DATA_URL, 50); } export const changeCurrentPAGE = page => ({ type, execute: state => ({...state, page}) })
Hinweis: Die type-Eigenschaft ist erforderlich (wenn Sie sie nicht hinzufügen, wird eine Ausnahme ausgelöst). Aber für uns spielt es überhaupt keine Rolle. Aus diesem Grund benötigen wir keine separate Datei mehr, in der die Arten von Aktionen aufgelistet sind.
PS: In diesem Artikel haben wir die Prinzipien von SRP und OCP, Polymorphismus, angewendet, die Bibliothek von Drittanbietern aufgegeben und unseren Code sauberer und wartbarer gemacht.