摘要:本文轉(zhuǎn)載自眾成翻譯譯者鏈接原文今天,我們在方法中使用中間件來管理我們的代碼中的復(fù)雜狀態(tài)變化。中間件是一個(gè)很好的地方。我們中間件我們將實(shí)現(xiàn)一些中間件它將代表我們處理異步請求。中間件位于動作和歸并器之間。讓我們創(chuàng)建我們的第一個(gè)中間件。
本文轉(zhuǎn)載自:眾成翻譯
譯者:iOSDevLog
鏈接:http://www.zcfy.cc/article/3810
原文:https://www.fullstackreact.com/30-days-of-react/day-21/
今天,我們在Redux方法中使用Redux中間件來管理我們的代碼中的復(fù)雜狀態(tài)變化。
昨天, 我們連接的點(diǎn)與Redux, 從工作通過歸并器, 更新行動的創(chuàng)造者, 并連接Redux到React組件。 Redux中間件 將解鎖更多的權(quán)力, 我們今天將會觸及。
Redux中間件中間件通常指的是軟件服務(wù), "粘合在一起" 在現(xiàn)有軟件中的獨(dú)立功能。對于Redux, 中間件提供了一個(gè) 第三方擴(kuò)展點(diǎn), 在分發(fā)動作和將分發(fā)交給歸并器之間:
[ Action ] [ Middleware ] [ Dispatcher ]
[ 動作 ] [ 中間件 ] [ 分發(fā) ]
中間件的示例包括日志記錄、崩潰報(bào)告、路由、處理異步請求等。
讓我們來處理異步請求, 就像對服務(wù)器的 HTTP 調(diào)用那樣。中間件是一個(gè)很好的地方。
我們中間件api我們將實(shí)現(xiàn)一些中間件, 它將代表我們處理異步請求。
中間件位于動作和歸并器之間。它可以監(jiān)聽所有的調(diào)度和執(zhí)行代碼與行動和當(dāng)前狀態(tài)的細(xì)節(jié)。中間件提供了一個(gè)強(qiáng)大的抽象。讓我們來看看如何使用它來管理我們自己的。
繼續(xù)我們從昨天開始的currentTime Redux的工作, 讓我們構(gòu)建我們的中間件, 以獲取當(dāng)前的時(shí)間從服務(wù)器, 我們用幾天前寫的真實(shí)從 API 服務(wù)獲取時(shí)間。
在我們做得太多之前, 讓我們從reducers.js 文件的rootReducer 中取出currentTime 的放到它自己的文件。我們離開了根歸并器在一個(gè)狀態(tài), 我們保持 currentTime 工作在根歸并器。通常來說, 我們將這些文件移動到他們自己的文檔中, 并使用rootReducer.js 文件 (我們稱之為reducers.js) 來保持主組合歸并器。
First, let"s pull the work into it"s own file in redux/currentTime.js. We"ll export two objects from here (and each reducer):首先, 讓我們把工作納入到它自己的redux/currentTime.js文件。我們將從這里 (和每個(gè)歸并器) 導(dǎo)出兩個(gè)對象:
initialState - 狀態(tài)樹的這個(gè)分支的初始狀態(tài)
reducer -這個(gè)分支的歸并器
import * as types from "./types"; export const initialState = { currentTime: new Date().toString(), } export const reducer = (state = initialState, action) => { switch(action.type) { case types.FETCH_NEW_TIME: return { ...state, currentTime: action.payload} default: return state; } } export default reducer
根歸并器用我們的currentTime , 我們將需要更新reducers.js 文件接受新文件到根歸并器。幸運(yùn)的是, 這很簡單:
import { combineReducers } from "redux"; import * as currentUser from "./currentUser"; import * as currentTime from "./currentTime"; export const rootReducer = combineReducers({ currentTime: currentTime.reducer, currentUser: currentUser.reducer, }) export const initialState = { currentTime: currentTime.initialState, currentUser: currentUser.initialState, } export default rootReducer
最后, 讓我們更新configureStore 函數(shù), 從文件中提取 rootReducer 和初始狀態(tài):
import { rootReducer, initialState } from "./reducers" // ... export const configureStore = () => { const store = createStore( rootReducer, initialState, ); return store; }返回到中間件
中間件基本上是一個(gè)接受store函數(shù), 它將返回一個(gè)接受next 函數(shù), 這將返回一個(gè)接受動作的函數(shù)。有點(diǎn)亂?讓我們看看這意味著什么。
可能是最簡單的中間件讓我們構(gòu)建最小的中間件, 我們可能能夠準(zhǔn)確地理解到底發(fā)生了什么, 以及如何將它添加到我們的棧中。
讓我們創(chuàng)建我們的第一個(gè)中間件。
現(xiàn)在, 中間件的簽名看起來像這樣:
const loggingMiddleware = (store) => (next) => (action) => { // Our middleware }
對這個(gè)中間件的事情很迷惑?別擔(dān)心, 我們都是第一次看到它。讓我們把它剝離回來一點(diǎn)點(diǎn), 拆解發(fā)生了什么事。上面的loggingMiddleware 描述可以像下面這樣重寫:
const loggingMiddleware = function(store) { // Called when calling applyMiddleware so // our middleware can have access to the store return function(next) { // next is the following action to be run // after this middleware return function(action) { // finally, this is where our logic lives for // our middleware. } } }
我們不需要擔(dān)心 怎么 被調(diào)用, 只是它確實(shí)得到了這個(gè)順序調(diào)用。讓我們增強(qiáng)我們的loggingMiddleware , 這樣我們實(shí)際上就可以注銷被調(diào)用的動作:
const loggingMiddleware = (store) => (next) => (action) => { // Our middleware console.log(`Redux Log:`, action) // call the next function next(action); }
Our middleware causes our store to, when every time an action is called, we"ll get a console.log with the details of the action.我們的中間件導(dǎo)致我們的存儲被調(diào)用,我們會得到一個(gè)console.log 動作細(xì)節(jié)。
為了將中間件應(yīng)用到我們的棧中, 我們將用這個(gè)恰當(dāng)命名的applyMiddleware 函數(shù)作為 createStore() 方法的第三個(gè)參數(shù)。
import { createStore, applyMiddleware } from "redux";
對于 應(yīng)用 中間件, 我們可以在 createStore() 方法中調(diào)用這個(gè) applyMiddleware() 函數(shù)。在我們的 src/redux/configureStore.js 文件中, 讓我們通過添加對applyMiddleware() 的調(diào)用來更新存儲創(chuàng)建:
const store = createStore( rootReducer, initialState, applyMiddleware( apiMiddleware, loggingMiddleware, ) );
現(xiàn)在我們的中間件已經(jīng)到位。在瀏覽器中打開控制臺以查看此演示所調(diào)用的所有動作。嘗試單擊打開控制臺的Update 按鈕.。
正如我們所看到的, 中間件使我們能夠在我們的Redux動作調(diào)用鏈中插入一個(gè)函數(shù)。在該函數(shù)中, 我們可以訪問該動作、狀態(tài), 而且我們還能夠分發(fā)其他動作。
我們希望編寫一個(gè)可以處理 API 請求的中間件函數(shù)。我們可以編寫一個(gè)中間件函數(shù), 它只偵聽與 API 請求對應(yīng)的動作。我們的中間件可以 "監(jiān)視" 具有特殊標(biāo)記的動作。例如, 我們可以有一個(gè) meta 對象的行動與 type 的 "api"。我們可以使用它來確保我們的中間件不處理與 API 請求無關(guān)的任何動作:
const apiMiddleware = store => next => action => { if (!action.meta || action.meta.type !== "api") { return next(action); } // This is an api request }
如果某個(gè)動作有一個(gè)帶有 "api",類型的元對象, 我們將在 apiMiddleware.中接收該請求。
讓我們轉(zhuǎn)換我們的updateTime()actionCreator, 將這些屬性包含到一個(gè) API 請求中。讓我們打開我們一直在使用的currentTime Redux模塊 (在src/redux/currentTime.js), 并找到fetchNewTime()函數(shù)定義。
讓我們把這個(gè)請求的 URL 傳遞給我們的meta 對象。我們甚至可以從調(diào)用動作創(chuàng)建者的內(nèi)部接受參數(shù):
const host = "https://andthetimeis.com" export const fetchNewTime = ({ timezone = "pst", str="now"}) => ({ type: types.FETCH_NEW_TIME, payload: new Date().toString(), meta: { type: "api", url: host + "/" + timezone + "/" + str + ".json" } })
當(dāng)我們按下按鈕更新的時(shí)間, 我們的apiMiddleware 將結(jié)束了在歸并器之前截取。對于我們在中間件中捕獲的任何調(diào)用, 我們可以將元對象拆分, 并使用這些選項(xiàng)進(jìn)行請求?;蛘? 我們可以通過fetch() API 將整個(gè)被消毒的meta 對象傳遞出去。
我們的 API 中間件需要采取的步驟:
從 meta 中查找請求 URL 并撰寫請求選項(xiàng)
提出要求
將請求轉(zhuǎn)換為 JavaScript 對象
回復(fù)Redux/用戶
讓我們采取這按步就班的步驟。首先, 關(guān)閉 URL 并創(chuàng)建fetchOptions 以傳遞到fetch()。我們將在下面的代碼中的注釋中列出這些步驟:
const apiMiddleware = store => next => action => { if (!action.meta || action.meta.type !== "api") { return next(action); } // This is an api request // Find the request URL and compose request options from meta const {url} = action.meta; const fetchOptions = Object.assign({}, action.meta); // Make the request fetch(url, fetchOptions) // convert the response to json .then(resp => resp.json()) .then(json => { // respond back to the user // by dispatching the original action without // the meta object let newAction = Object.assign({}, action, { payload: json.dateString }); delete newAction.meta; store.dispatch(newAction); }) } export default apiMiddleware
我們有幾個(gè)選項(xiàng), 我們?nèi)绾位貜?fù)到Redux鏈中的用戶。就個(gè)人而言, 我們更喜歡用相同的類型響應(yīng)請求被激發(fā), 而沒有 meta 標(biāo)記, 并將響應(yīng)體作為新動作的 payload 有效負(fù)載 。
這樣, 我們不需要改變我們的Redux歸并器來管理響應(yīng)任何不同的, 如果我們沒有提出要求。
我們也不限于一個(gè)單一的響應(yīng)。假設(shè)我們的用戶在請求完成時(shí)通過了onSuccess 回調(diào)來調(diào)用。我們可以調(diào)用這個(gè)onSuccess 回調(diào), 然后發(fā)送備份鏈:
const apiMiddleware = store => next => action => { if (!action.meta || action.meta.type !== "api") { return next(action); } // This is an api request // Find the request URL and compose request options from meta const {url} = action.meta; const fetchOptions = Object.assign({}, action.meta); // Make the request fetch(url, fetchOptions) // convert the response to json .then(resp => resp.json()) .then(json => { if (typeof action.meta.onSuccess === "function") { action.meta.onSuccess(json); } return json; // For the next promise in the chain }) .then(json => { // respond back to the user // by dispatching the original action without // the meta object let newAction = Object.assign({}, action, { payload: json.dateString }); delete newAction.meta; store.dispatch(newAction); }) }
這里的可能性幾乎是無止境的。讓我們添加apiMiddleware 到我們的鏈通過它更新configureStore() 函數(shù):
import { createStore, applyMiddleware } from "redux"; import { rootReducer, initialState } from "./reducers" import loggingMiddleware from "./loggingMiddleware"; import apiMiddleware from "./apiMiddleware"; export const configureStore = () => { const store = createStore( rootReducer, initialState, applyMiddleware( apiMiddleware, loggingMiddleware, ) ); return store; } export default configureStore;
請注意, 我們不必更改視圖的 _任意_代碼 以更新數(shù)據(jù)在狀態(tài)樹中的填充方式。很漂亮吧?
這個(gè)中間件非常簡單, 但它是構(gòu)建它的良好基礎(chǔ)。您是否可以考慮如何實(shí)現(xiàn)緩存服務(wù), 以便我們不需要對已有的數(shù)據(jù)進(jìn)行請求?如何讓一個(gè)跟蹤掛起的請求, 這樣我們就可以為未完成的請求顯示一個(gè)微調(diào)框?
太棒了!現(xiàn)在我們真的是Redux忍者。我們已經(jīng)征服了Redux大山, 并準(zhǔn)備繼續(xù)下一步的行動。在我們?nèi)ブ? 但是..。我們已經(jīng)完成了3周!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87166.html
摘要:今天我們將討論創(chuàng)建組件的最終方案,即無狀態(tài)函數(shù)的純組件。今天我們正在研究一種處理提出的復(fù)雜數(shù)據(jù)的方法,稱為體系結(jié)構(gòu)。第天部署介紹今天,我們將探討部署我們的應(yīng)用所涉及的不同部分,以便外界可以使用我們的應(yīng)用。 本文轉(zhuǎn)載自:眾成翻譯譯者:iOSDevLog鏈接:http://www.zcfy.cc/article/3758原文:https://www.fullstackreact.com/3...
摘要:在方法中處理數(shù)據(jù)有三不同的角色派發(fā)器儲存視圖層我們的組件的主要思想是有一個(gè)單一源儲存他們只能通過觸發(fā)更新。這些操作負(fù)責(zé)調(diào)用派發(fā)器可以訂閱更改并相應(yīng)地更新自己的數(shù)據(jù)。與不同不使用派發(fā)器而是使用純函數(shù)來定義數(shù)據(jù)變異函數(shù)。 本文轉(zhuǎn)載自:眾成翻譯譯者:iOSDevLog鏈接:http://www.zcfy.cc/article/3812原文:https://www.fullstackreact...
摘要:去營救有一種方法我們把我們的歸約器分成多個(gè)歸約器每個(gè)都只負(fù)責(zé)狀態(tài)樹的葉子。此外我們還學(xué)習(xí)了如何擴(kuò)展以使用多個(gè)歸約器和動作以及多個(gè)連接的組件。 本文轉(zhuǎn)載自:眾成翻譯譯者:iOSDevLog鏈接:http://www.zcfy.cc/article/3825原文:https://www.fullstackreact.com/30-days-of-react/day-20/ 使用Redux,...
摘要:包包含由團(tuán)隊(duì)提供的測試實(shí)用程序。將在一個(gè)名為的目錄中自動查找整個(gè)樹中的測試文件是的帶有下劃線。讓我們?yōu)闀r(shí)間軸組件創(chuàng)建第一個(gè)測試。其中之一是命令?,F(xiàn)在我們已經(jīng)編寫了第一個(gè)測試并確認(rèn)了我們的設(shè)置我們將在明天開始測試我們的時(shí)間軸組件。 本文轉(zhuǎn)載自:眾成翻譯譯者:iOSDevLog鏈接:http://www.zcfy.cc/article/3807原文:https://www.fullstac...
摘要:歸約器函數(shù)負(fù)責(zé)返回應(yīng)用當(dāng)前全局狀態(tài)的表示形式。當(dāng)我們在存儲上發(fā)送操作時(shí)將使用應(yīng)用的當(dāng)前狀態(tài)和導(dǎo)致狀態(tài)更新的操作來調(diào)用此歸約器函數(shù)?;氐轿覀兊臍w約器我們可以檢查的動作類型并采取適當(dāng)?shù)牟襟E創(chuàng)建下一個(gè)狀態(tài)。我們將處理動作創(chuàng)造者中歸約器的副作用。 本文轉(zhuǎn)載自:眾成翻譯譯者:iOSDevLog鏈接:http://www.zcfy.cc/article/3811原文:https://www.ful...
閱讀 2795·2021-11-22 14:45
閱讀 2941·2021-09-10 11:26
閱讀 3265·2021-09-07 10:18
閱讀 2231·2019-08-30 14:08
閱讀 633·2019-08-29 12:22
閱讀 1400·2019-08-26 13:48
閱讀 2546·2019-08-26 10:24
閱讀 1163·2019-08-23 18:35