摘要:中間件對異步的實(shí)現(xiàn)非常重要,因?yàn)樵谥暗奈恼轮形覀冋劦剑且粋€行為抽象,只是一個對象,是一個純函數(shù),不應(yīng)該有調(diào)用和副作用的操作。這個函數(shù)并不需要保持純凈,它還可以帶有副作用,包括執(zhí)行異步請求。那么如何在中進(jìn)行網(wǎng)絡(luò)請求標(biāo)準(zhǔn)的做法是使用。
在之前的淺談Flux架構(gòu)及Redux實(shí)踐一文中我們初步的談及了Redux的數(shù)據(jù)流思想,并做了一個簡單的加減器。但是還沒有接觸到Redux更多常用的場景,異步操作、API調(diào)用,如何連接到UI層等,Redux可以與很多框架搭配包括Vue、React甚至是純JavaScript。后面我們會用一個實(shí)例--通過github API獲取個人信息,來將Redux middleware、async action、連接到React貫穿其中。先看看我們最后寫的demo的樣子。
Middleware與異步Action依然先看看Redux作者Dam的描述:
It provides a third-party extension point between dispatching an
action, and the moment it reaches the reducer.
我的理解是,middleware提供了一個你可以修改action的機(jī)制,這和Express/Koa的中間件有些類似,只不過這里的中間件主要是操作action。中間件對異步的action實(shí)現(xiàn)非常重要,因?yàn)樵谥暗奈恼轮形覀冋劦剑琣ction是一個行為抽象,只是一個對象,reducer是一個純函數(shù),不應(yīng)該有API調(diào)用和副作用的操作。那么怎么解決異步的問題?我們肯定不能在reducer中寫,那么就考慮到了action -> reducer這個過程,這就是redux middleware:
action -> middleware modify action -> reducer
它提供的是位于 action 被發(fā)起之后,到達(dá) reducer 之前的擴(kuò)展點(diǎn)。 你可以利用 Redux middleware 來進(jìn)行日志記錄、創(chuàng)建崩潰報(bào)告、調(diào)用異步接口或者路由等等。
在上一篇文章中我們使用的同步action,action creator返回的是一個對象,但是異步action可以是一個函數(shù),雖然函數(shù)也是對象,這里我們只是為了區(qū)分兩種不同的情況。通過使用指定的 middleware,action creator可以返回函數(shù)。這時,這個 action creator 就成為了 thunk。當(dāng) action creator 返回函數(shù)時,這個函數(shù)會被 Redux Thunk middleware 執(zhí)行。這個函數(shù)并不需要保持純凈,它還可以帶有副作用,包括執(zhí)行異步 API 請求。這個函數(shù)還可以 dispatch action,就像 dispatch 前面定義的同步 action 一樣。那么如何在action中進(jìn)行網(wǎng)絡(luò)請求?標(biāo)準(zhǔn)的做法是使用 Redux Thunk middleware。要引入 redux-thunk 這個專門的庫才能使用。
搭建工作流我們將采用ES6語法,webpack進(jìn)行打包,webpack-dev-server啟一個本地服務(wù)器,然后用HMR技術(shù)進(jìn)行React熱加載,看看webpack配置信息:
var webpack = require("webpack"); var OpenBrowserPlugin = require("open-browser-webpack-plugin"); module.exports = { entry: { index: [ "webpack/hot/dev-server", "webpack-dev-server/client?http://localhost:8080", "./src/index.js", ] }, output: { path: "./build", filename: "[name].js", }, devtool: "source-map", module: { loaders: [{ test: /.js$/, loader: "babel", query: { presets: ["es2015", "stage-0", "react"], }, }, { test: /.less$/, loader: "style!css!less", }], }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), new OpenBrowserPlugin({ url: "http://localhost:8080" }), ] };
其中open-browser-webpack-plugin插件將會幫助我們自動打開瀏覽器,用babel進(jìn)行es編譯,less來維護(hù)我們的css樣式,以及使用dev-tool來生成source map,HotModuleReplacementPlugin來進(jìn)行熱更新。
再看看我們最后的目錄結(jié)構(gòu):
├── build │?? ├── index.html │?? └── index.js ├── node_modules ├── package.json ├── src │?? ├── actions │?? │?? └── actions.js │?? ├── components │?? │?? ├── index.js │?? │?? ├── Profile │?? │?? │?? ├── Profile.js │?? │?? │?? └── Profile.less │?? │?? └── Search │?? │?? ├── Search.js │?? │?? └── Search.less │?? ├── containers │?? │?? ├── App.js │?? │?? ├── App.less │?? │?? └── test.less │?? ├── index.html │?? ├── index.js │?? └── reducers │?? └── reducers.js └── webpack.config.js
其中containers放置我們的容器組件,components放置展示性組件,打包入口是index.js。
Demo Reduxstate
使用Redux非常重要的一點(diǎn)就是設(shè)計(jì)好頂層的state,在demo中我們需要的state大概長這個樣子:
{ isFetchingData, // boolean username, // string profile, // object }
其中isFetchingData是網(wǎng)絡(luò)請求的狀態(tài),正在拉取數(shù)據(jù)為true,username是我們要獲取用戶信息的名字,profile是我們拉取用戶的詳細(xì)信息,這個將會是一個Ajax請求,最后由github API提供。
actions
同步action我們不再講述,上一篇文章已經(jīng)說得比較清楚,這里我們重點(diǎn)說異步action,app的所有action如下:
export const GET_INFO = "GET_INFO"; // 獲取用戶信息 export const FETCHING_DATA = "FETCHING_DATA"; // 拉取狀態(tài) export const RECEIVE_USER_DATA = "RECEIVE_USER_DATA"; //接收到拉取的狀態(tài) // async action creator export function fetchUserInfo(username) { return function (dispatch) { dispatch(fetchingData(true)); return fetch(`https://api.github.com/users/${username}`) .then(response => { console.log(response); return response.json(); }) .then(json => { console.log(json); return json; }) .then((json) => { dispatch(receiveUserData(json)) }) .then(() => dispatch(fetchingData(false))); }; }
上面網(wǎng)絡(luò)請求用到了fetch這個API,它會返回一個Promise,還比較新可以使用社區(qū)提供的polyfill或者使用純粹的XHR都行,這都不是重點(diǎn)。我們看看這個action生成函數(shù)返回了一個函數(shù),并且在這個函數(shù)中還有dispatch操作,我們通過中間件傳入的dispatch可以用來dispatch actions。在上面的promise鏈?zhǔn)街惺紫任覀兇蛴×薵ithub API返回Response object,然后輸出了json格式的數(shù)據(jù),然后dispatch了RECEIVE_USER_DATA這個action表示接收到了網(wǎng)絡(luò)請求,并需要修改state(注:這里我們沒有考慮網(wǎng)絡(luò)請求失敗的情況),最后我們dispatch了FETCHING_DATA并告訴對應(yīng)reducer下一個state的isFetchingData為false,表示數(shù)據(jù)拉取完畢。
reducer
這里看看最核心的reducer,操作profile這一塊的:
function profile(state = {}, action) { switch (action.type) { case GET_INFO: return Object.assign({}, state, { username: action.username, }); case RECEIVE_USER_DATA: return Object.assign({}, state, action.profile); default: return state; } } function isFetchingData() {...} function username() {...} const rootReducer = combineReducers({ isFetchingData, username, profile, }); export default rootReducer;
將拉取到的profile對象assign到之前的state,最后通過combineReducers函數(shù)合并為一個reducer。
連接到React我們通過react-redux提供的connect方法與Provider來連接到React,Provider主要的作用是包裝我們的容器組件,connect用于將redux與react進(jìn)行連接,connect() 允許你從 Redux store 中指定準(zhǔn)確的 state 到你想要獲取的組件中。這讓你能獲取到任何級別顆粒度的數(shù)據(jù),了解更多可以參考它的API,這里我們不再敖述。它的形式可以是這樣:
function mapStateToProps(state) { return { profile: state.profile, isFetchingData: state.isFetchingData, }; } function mapDispatchToProps(dispatch) { return { fetchUserInfo: (username) => dispatch(fetchUserInfo(username)) }; } class App extends Component { render() { const { fetchUserInfo, profile, isFetchingData } = this.props; return (); } } export default connect( mapStateToProps, mapDispatchToProps )(App);{"name" in profile ? : ""}
connect是個可以執(zhí)行兩次的柯里化函數(shù),第一次傳入的參數(shù)相當(dāng)于一系列的定制化東西,第二次傳入的是你要連接的React組件,然后返回一個新的React組件。第一次執(zhí)行時傳入的參數(shù)是mapStateToProps, mapDispatchToProps, mergeProps, options。也就是說這里相當(dāng)于幫組容器選擇它在整個Store中所需的state與dispatch回調(diào),這些將會被connect以Props的形式綁定到App容器,我們可以通過React開發(fā)者工具看到這一點(diǎn):
第一次執(zhí)行,選擇需要的state,第二次傳入App容器組件然后返回新的組件。然后創(chuàng)建整個應(yīng)用的store:
const loggerMiddleware = createLogger(); const store = createStore( rootReducer, compose( applyMiddleware( thunkMiddleware, loggerMiddleware, ), window.devToolsExtension ? window.devToolsExtension() : f => f ) );
這里我們用到了兩個中間件,loggerMiddleware用于輸出我們每一次的action,可以明確的看到每次action后state的前后狀態(tài),thunkMiddleware用于網(wǎng)絡(luò)請求處理,最后window.devToolsExtension ? window.devToolsExtension() : f => f是為了連接我們的redux-dev-tool,可以明確的看到我們dispatch的action,還能達(dá)到時間旅行的效果。最后通過Provider輸入我們的store,整個應(yīng)用就跑起來啦!
let mountRoot = document.getElementById("app"); ReactDOM.render(Run, mountRoot );
命令行輸入npm run dev,整個應(yīng)用就跑起來了,在輸入框輸入Jiavan,我們來看看action與數(shù)據(jù)流:
在console面板,logger中間件為我們打印除了每一次dispatch action以及前后的state值,是不是非常直觀,然而厲害的還在后面。redux-dev-tool可以直接查看我們state tree以及對action做undo操作達(dá)到時間旅行的效果!
完整的demo在文章最后將會貼出,現(xiàn)在總結(jié)下:首先我們規(guī)劃了整個應(yīng)用的state,然后進(jìn)行數(shù)據(jù)流層的代碼開發(fā),同步異步action的編寫以及reducer的開發(fā),再通過選擇我們?nèi)萜鹘M件所需的state與dispatch回調(diào)通過connect方法綁定后輸出新的組件,通過創(chuàng)建store與Provider將其與React連接,這樣整個應(yīng)用的任督二脈就被打通了。最后極力推薦Redux的官方文檔。
完整demo -> https://github.com/Jiavan/rea...
運(yùn)行
1. npm install 2. webpack 3. npm run dev參考文章
http://cn.redux.js.org/docs/r...
http://www.jianshu.com/p/ab9e...
原文出處 https://github.com/Jiavan/jia... 覺得對你有幫助就給個star吧
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/80220.html
摘要:異步實(shí)現(xiàn)設(shè)計(jì)需要增加三種通知異步請求發(fā)起的異步請求成功的異步請求失敗的示例代碼如下返回參數(shù)完全可以自定義。這種分別在請求開始前,請求成功后,請求失敗后發(fā)送。表示數(shù)據(jù)的有效性,他的作用是在異步請求發(fā)送失敗后,告訴當(dāng)前的數(shù)據(jù)是過時的數(shù)據(jù)。 說明:對Redux不了解的同學(xué)可先看看這篇文章Redux技術(shù)架構(gòu)簡介(一) 前言 這里說的Redux異步實(shí)現(xiàn),是專指Redux中的異步Action實(shí)現(xiàn),...
摘要:舉例來說一個異步的請求場景,可以如下實(shí)現(xiàn)任何異步的邏輯都可以,如等等也可以使用的和。實(shí)際上在中,一個就是一個函數(shù)。 書籍完整目錄 3.4 redux 異步 showImg(https://segmentfault.com/img/bVyou8); 在大多數(shù)的前端業(yè)務(wù)場景中,需要和后端產(chǎn)生異步交互,在本節(jié)中,將詳細(xì)講解 redux 中的異步方案以及一些異步第三方組件,內(nèi)容有: redu...
摘要:基本流程創(chuàng)建帶有三個方法發(fā)出處理數(shù)據(jù)每次后的數(shù)據(jù)即得到的數(shù)據(jù)即一個新的所以是一個對象每次都是上次返回的值所以用即返回的新狀態(tài)新的即本次的返回值所以每次都是往空的對象里先推再新增屬性或改變原來屬性的值層通過方法設(shè)置監(jiān)聽函數(shù)一旦發(fā)生變化就會 基本流程 1.創(chuàng)建store,帶有三個方法:store.dispatch,store.subscribe,store.getState import ...
摘要:在函數(shù)式編程中,異步操作修改全局變量等與函數(shù)外部環(huán)境發(fā)生的交互叫做副作用通常認(rèn)為這些操作是邪惡骯臟的,并且也是導(dǎo)致的源頭。 注:這篇是17年1月的文章,搬運(yùn)自本人 blog... https://github.com/BuptStEve/... 零、前言 在上一篇中介紹了 Redux 的各項(xiàng)基礎(chǔ) api。接著一步一步地介紹如何與 React 進(jìn)行結(jié)合,并從引入過程中遇到的各個痛點(diǎn)引出 ...
摘要:是官方文檔中用到的異步組件,實(shí)質(zhì)就是一個中間件,簡單來說就是一個封裝表達(dá)式的函數(shù),封裝的目的是延遲執(zhí)行表達(dá)式。這時我們需要對一般異步中間件進(jìn)行處理。 曾經(jīng)前端的革新是以Ajax的出現(xiàn)為分水嶺,現(xiàn)代應(yīng)用中絕大部分頁面渲染會以異步流的方式進(jìn)行。在Redux中,如果要發(fā)起異步請求,最合適的位置是在action creator中實(shí)現(xiàn)。但我們之前了解到的action都是同步情況,因此需要引入中間...
閱讀 3297·2021-11-24 09:39
閱讀 3878·2021-11-22 09:34
閱讀 4824·2021-08-11 11:17
閱讀 1067·2019-08-29 13:58
閱讀 2580·2019-08-28 18:18
閱讀 547·2019-08-26 12:24
閱讀 834·2019-08-26 12:14
閱讀 739·2019-08-26 11:58