Maison > Article > interface Web > Qu’est-ce que le middleware React ?
Dans React, le middleware est une fonction. La méthode store.dispatch a été modifiée et d'autres fonctions ont été ajoutées entre les deux étapes d'émission d'Action et d'exécution du middleware couramment utilisé, il suffit de référencer le middleware couramment utilisé ; modules écrits par d'autres.
Afin de comprendre le middleware, pensons du point de vue du middleware. auteur du framework Question : Si vous deviez ajouter des fonctionnalités, où l'ajouteriez-vous ?
(1) Réducteur : fonction pure, qui remplit uniquement la fonction de calcul de l'état. Elle ne convient pas à d'autres fonctions et ne peut pas être exécutée car théoriquement, les fonctions pures ne peuvent pas effectuer d'opérations de lecture et d'écriture.
(2) Vue : correspond à l'État un à un. Elle peut être considérée comme la couche visuelle de l'État et ne convient pas à d'autres fonctions.
(3) Action : L'objet qui stocke les données, c'est-à-dire le porteur du message, ne peut être exploité que par d'autres et ne peut effectuer aucune opération par lui-même.
Après de longues délibérations, seule l'étape d'envoi d'Action, c'est-à-dire la méthode store.dispatch(), peut ajouter des fonctionnalités. Par exemple, pour ajouter une fonction de journalisation et imprimer l'action et l'état, vous pouvez modifier store.dispatch comme suit.
let next = store.dispatch; store.dispatch = function dispatchAndLog(action) { console.log('dispatching', action); next(action); console.log('next state', store.getState()); }
Dans le code ci-dessus, store.dispatch est redéfini et la fonction d'impression est ajoutée avant et après l'envoi de l'action. C'est le prototype du middleware.
Middleware est une fonction qui modifie la méthode store.dispatch et ajoute d'autres fonctions entre les deux étapes d'émission d'Action et d'exécution de Réducteur. Les middlewares couramment utilisés sont prêts à l'emploi, il suffit de se référer aux modules écrits par d'autres.
Ce tutoriel n'explique pas comment écrire un middleware, car le middleware couramment utilisé est prêt à l'emploi. Il vous suffit de référencer les modules écrits par. d'autres. C'est tout. Par exemple, le middleware de journalisation de la section précédente dispose d'un module redux-logger prêt à l'emploi. Ici, nous présentons uniquement comment utiliser le middleware.
import { applyMiddleware, createStore } from 'redux'; import createLogger from 'redux-logger'; const logger = createLogger(); const store = createStore( reducer, applyMiddleware(logger) );
Dans le code ci-dessus, redux-logger fournit un générateur createLogger, qui peut générer un enregistreur de log middleware. Ensuite, placez-le dans la méthode applyMiddleware et transmettez-le dans la méthode createStore pour terminer l'amélioration fonctionnelle de store.dispatch().
Il y a deux points à noter ici :
(1) La méthode createStore peut accepter l'état initial de l'ensemble de l'application comme paramètre. Dans ce cas, applyMiddleware sera le troisième paramètre.
const store = createStore( reducer, initial_state, applyMiddleware(logger) );
(2) L'ordre des middlewares doit être particulier.
const store = createStore( reducer, applyMiddleware(thunk, promise, logger) );
Dans le code ci-dessus, les trois paramètres de la méthode applyMiddleware sont trois middlewares. Certains middlewares ont des exigences de commande, alors vérifiez la documentation avant de les utiliser. Par exemple, l'enregistreur doit être placé à la fin, sinon les résultats de sortie seront incorrects.
En voyant cela, vous vous demandez peut-être, que fait exactement la méthode applyMiddlewares ?
C'est une méthode native de Redux. Sa fonction est de former un tableau de tous les middlewares et de l'exécuter en séquence. Ci-dessous son code source.
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer); var dispatch = store.dispatch; var chain = []; var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) }; chain = middlewares.map(middleware => middleware(middlewareAPI)); dispatch = compose(...chain)(store.dispatch); return {...store, dispatch} } }
Dans le code ci-dessus, tous les middlewares sont placés dans une chaîne de tableaux, puis imbriqués pour l'exécution, et enfin store.dispatch est exécuté. Comme vous pouvez le constater, les deux méthodes getState et dispatch sont disponibles dans le middleware (middlewareAPI).
Après avoir compris le middleware, vous pouvez gérer les opérations asynchrones.
Les opérations synchrones ne doivent émettre qu'une seule action. La différence entre les opérations asynchrones est qu'elles doivent émettre trois actions.
Action lorsque l'opération est initiée
Action lorsque l'opération est réussie
Action lorsque l'opération échoue Action
En prenant comme exemple la récupération des données du serveur, trois actions peuvent être écrites de deux manières différentes.
// 写法一:名称相同,参数不同 { type: 'FETCH_POSTS' } { type: 'FETCH_POSTS', status: 'error', error: 'Oops' } { type: 'FETCH_POSTS', status: 'success', response: { ... } } // 写法二:名称不同 { type: 'FETCH_POSTS_REQUEST' } { type: 'FETCH_POSTS_FAILURE', error: 'Oops' } { type: 'FETCH_POSTS_SUCCESS', response: { ... } }
En plus des différents types d'actions, l'état des opérations asynchrones doit également être modifié pour refléter différents états de fonctionnement. Vous trouverez ci-dessous un exemple d'État.
let state = { // ... isFetching: true, didInvalidate: true, lastUpdated: 'xxxxxxx' };
Dans le code ci-dessus, l'attribut isFetching of State indique si les données sont récupérées. didInvalidate indique si les données sont obsolètes et lastUpdated indique l'heure de la dernière mise à jour.
Maintenant, l'idée de l'ensemble du fonctionnement asynchrone est très claire.
Lorsque l'opération démarre, envoyez une action, déclenchez la mise à jour de l'état à l'état « opérationnel » et affichez les nouveaux rendus
Une fois l'opération terminée, renvoyez-la à nouveau. Une action est déclenchée, l'état est mis à jour à l'état "opération terminée" et la vue est à nouveau rendue
Les opérations asynchrones doivent envoyer au moins deux actions : l'utilisateur déclenche la première action, qui est la même que l'opération synchrone, pas de problème, comment le système peut-il envoyer automatiquement l'action ; deuxième Action en fin d’opération ?
Le secret est dans Action Creator.
class AsyncApp extends Component { componentDidMount() { const { dispatch, selectedPost } = this.props dispatch(fetchPosts(selectedPost)) } // ...
Le code ci-dessus est un exemple de composant asynchrone. Après un chargement réussi (méthodecomponentDidMount), il envoie (méthode de répartition) une action pour demander des données fetchPosts (selectedSubreddit) au serveur. Le fetchPosts ici est Action Creator.
Ce qui suit est le code de fetchPosts, le point clé est dedans.
const fetchPosts = postTitle => (dispatch, getState) => { dispatch(requestPosts(postTitle)); return fetch(`/some/API/${postTitle}.json`) .then(response => response.json()) .then(json => dispatch(receivePosts(postTitle, json))); }; }; // 使用方法一 store.dispatch(fetchPosts('reactjs')); // 使用方法二 store.dispatch(fetchPosts('reactjs')).then(() => console.log(store.getState()) );
上面代码中,fetchPosts是一个Action Creator(动作生成器),返回一个函数。这个函数执行后,先发出一个Action(requestPosts(postTitle)),然后进行异步操作。拿到结果后,先将结果转成 JSON 格式,然后再发出一个 Action( receivePosts(postTitle, json))。
上面代码中,有几个地方需要注意。
(1)fetchPosts返回了一个函数,而普通的 Action Creator 默认返回一个对象。
(2)返回的函数的参数是dispatch和getState这两个 Redux 方法,普通的 Action Creator 的参数是 Action 的内容。
(3)在返回的函数之中,先发出一个 Action(requestPosts(postTitle)),表示操作开始。
(4)异步操作结束之后,再发出一个 Action(receivePosts(postTitle, json)),表示操作结束。
这样的处理,就解决了自动发送第二个 Action 的问题。但是,又带来了一个新的问题,Action 是由store.dispatch方法发送的。而store.dispatch方法正常情况下,参数只能是对象,不能是函数。
这时,就要使用中间件redux-thunk。
import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import reducer from './reducers'; // Note: this API requires redux@>=3.1.0 const store = createStore( reducer, applyMiddleware(thunk) );
上面代码使用redux-thunk中间件,改造store.dispatch,使得后者可以接受函数作为参数。
因此,异步操作的第一种解决方案就是,写出一个返回函数的 Action Creator,然后使用redux-thunk中间件改造store.dispatch。
既然 Action Creator 可以返回函数,当然也可以返回其他值。另一种异步操作的解决方案,就是让 Action Creator 返回一个 Promise 对象。
这就需要使用redux-promise中间件。
import { createStore, applyMiddleware } from 'redux'; import promiseMiddleware from 'redux-promise'; import reducer from './reducers'; const store = createStore( reducer, applyMiddleware(promiseMiddleware) );
这个中间件使得store.dispatch方法可以接受 Promise 对象作为参数。这时,Action Creator 有两种写法。写法一,返回值是一个 Promise 对象。
const fetchPosts = (dispatch, postTitle) => new Promise(function (resolve, reject) { dispatch(requestPosts(postTitle)); return fetch(`/some/API/${postTitle}.json`) .then(response => { type: 'FETCH_POSTS', payload: response.json() }); });
写法二,Action 对象的payload属性是一个 Promise 对象。这需要从redux-actions模块引入createAction方法,并且写法也要变成下面这样。
import { createAction } from 'redux-actions'; class AsyncApp extends Component { componentDidMount() { const { dispatch, selectedPost } = this.props // 发出同步 Action dispatch(requestPosts(selectedPost)); // 发出异步 Action dispatch(createAction( 'FETCH_POSTS', fetch(`/some/API/${postTitle}.json`) .then(response => response.json()) )); }
上面代码中,第二个dispatch方法发出的是异步 Action,只有等到操作结束,这个 Action 才会实际发出。注意,createAction的第二个参数必须是一个 Promise 对象。
看一下redux-promise的源码,就会明白它内部是怎么操作的。
export default function promiseMiddleware({ dispatch }) { return next => action => { if (!isFSA(action)) { return isPromise(action) ? action.then(dispatch) : next(action); } return isPromise(action.payload) ? action.payload.then( result => dispatch({ ...action, payload: result }), error => { dispatch({ ...action, payload: error, error: true }); return Promise.reject(error); } ) : next(action); }; }
从上面代码可以看出,如果 Action 本身是一个 Promise,它 resolve 以后的值应该是一个 Action 对象,会被dispatch方法送出(action.then(dispatch)),但 reject 以后不会有任何动作;如果 Action 对象的payload属性是一个 Promise 对象,那么无论 resolve 和 reject,dispatch方法都会发出 Action。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!