Ecrire redux par SOLID

Dans cet article, nous aborderons les actions d'écriture et de réduction. Pour commencer, considérons un «flux» typique, dans lequel nous effectuons les opérations suivantes (en outre, nous retravaillons tout pour que notre code réponde aux principes SOLIDES).

1. créer un fichier avec des constantes (ici on enregistre les noms des types d'actions)

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. créez un fichier où nous décrivons les actions (ici nous faisons une demande de comptes utilisateurs et de pagination). Toujours dans l'exemple, redux-thunk a été utilisé (nous refuserons en outre des dépendances similaires):

 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. nous écrivons réducteur

 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. configurer le magasin (utiliser le middleware thunkMiddleware)

 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. connectez le composant à redux
 const mapDispatchToProps = (dispatch)=>{ return { onRequestBigData: (event) =>{ dispatch(requestBigDataAction()); } } }; 

connecter les boutons de pagination au redux

 const mapDispatchToProps = (dispatch)=>{ return { onChangePage: (page) =>{ dispatch(changeCurrentPAGE(page)); } } }; 

Problème: notre réducteur est une grande déclaration d'interrupteur, par conséquent, lors de l'ajout d'une nouvelle action ou de la modification de son comportement, nous devons changer notre réducteur, ce qui viole les principes de SOlid (le principe de la responsabilité exclusive et le principe d'ouverture / de proximité).

Solution: le polymorphisme nous aidera. Ajoutez à chaque action la méthode d'exécution, qui appliquera la mise à jour et retournera l'état mis à jour. Ensuite, notre réducteur prendra la forme

 export const MainReducer = (state = initialState, action) => { if(typeof action.execute === 'function') return action.execute(state); return state; }; 

maintenant, lorsque vous ajoutez une nouvelle action, nous n'avons pas besoin de changer de réducteur, et cela ne se transforme pas en un énorme monstre.

Ensuite, abandonnez le redux-thunk et réécrivez les actions

 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'; // import thunkMiddleware from 'redux-thunk'; import {MainReducer} from './reducers/mainReducer'; const store = createStore(MainReducer); ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root')); 

aller au composant connecté, dont l'action est asynchrone (il faudra la corriger légèrement)

 const mapDispatchToProps = (dispatch)=>{ return { onRequestBigData: (event) =>{ requestBigDataAction(dispatch); }, } }; 

et passer aux actions elles-mêmes et leur ajouter la méthode d'exécution

 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}) }) 

Remarque: la propriété type est obligatoire (si vous ne l'ajoutez pas, une exception sera levée). Mais pour nous, cela n'a pas d'importance du tout. C'est pourquoi nous n'avons plus besoin d'un fichier séparé répertoriant les types d'actions.

PS: Dans cet article, nous avons appliqué les principes de SRP et OCP, le polymorphisme, abandonné la bibliothèque tierce et rendu notre code plus propre et maintenable.

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


All Articles