摘要:中間件對異步的實現(xiàn)非常重要,因為在之前的文章中我們談到,是一個行為抽象,只是一個對象,是一個純函數,不應該有調用和副作用的操作。這個函數并不需要保持純凈,它還可以帶有副作用,包括執(zhí)行異步請求。那么如何在中進行網絡請求標準的做法是使用。
在之前的淺談Flux架構及Redux實踐一文中我們初步的談及了Redux的數據流思想,并做了一個簡單的加減器。但是還沒有接觸到Redux更多常用的場景,異步操作、API調用,如何連接到UI層等,Redux可以與很多框架搭配包括Vue、React甚至是純JavaScript。后面我們會用一個實例--通過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的機制,這和Express/Koa的中間件有些類似,只不過這里的中間件主要是操作action。中間件對異步的action實現(xiàn)非常重要,因為在之前的文章中我們談到,action是一個行為抽象,只是一個對象,reducer是一個純函數,不應該有API調用和副作用的操作。那么怎么解決異步的問題?我們肯定不能在reducer中寫,那么就考慮到了action -> reducer這個過程,這就是redux middleware:
action -> middleware modify action -> reducer
它提供的是位于 action 被發(fā)起之后,到達 reducer 之前的擴展點。 你可以利用 Redux middleware 來進行日志記錄、創(chuàng)建崩潰報告、調用異步接口或者路由等等。
在上一篇文章中我們使用的同步action,action creator返回的是一個對象,但是異步action可以是一個函數,雖然函數也是對象,這里我們只是為了區(qū)分兩種不同的情況。通過使用指定的 middleware,action creator可以返回函數。這時,這個 action creator 就成為了 thunk。當 action creator 返回函數時,這個函數會被 Redux Thunk middleware 執(zhí)行。這個函數并不需要保持純凈,它還可以帶有副作用,包括執(zhí)行異步 API 請求。這個函數還可以 dispatch action,就像 dispatch 前面定義的同步 action 一樣。那么如何在action中進行網絡請求?標準的做法是使用 Redux Thunk middleware。要引入 redux-thunk 這個專門的庫才能使用。
搭建工作流我們將采用ES6語法,webpack進行打包,webpack-dev-server啟一個本地服務器,然后用HMR技術進行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進行es編譯,less來維護我們的css樣式,以及使用dev-tool來生成source map,HotModuleReplacementPlugin來進行熱更新。
再看看我們最后的目錄結構:
├── 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非常重要的一點就是設計好頂層的state,在demo中我們需要的state大概長這個樣子:
{ isFetchingData, // boolean username, // string profile, // object }
其中isFetchingData是網絡請求的狀態(tài),正在拉取數據為true,username是我們要獲取用戶信息的名字,profile是我們拉取用戶的詳細信息,這個將會是一個Ajax請求,最后由github API提供。
actions
同步action我們不再講述,上一篇文章已經說得比較清楚,這里我們重點說異步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))); }; }
上面網絡請求用到了fetch這個API,它會返回一個Promise,還比較新可以使用社區(qū)提供的polyfill或者使用純粹的XHR都行,這都不是重點。我們看看這個action生成函數返回了一個函數,并且在這個函數中還有dispatch操作,我們通過中間件傳入的dispatch可以用來dispatch actions。在上面的promise鏈式中首先我們打印了github API返回Response object,然后輸出了json格式的數據,然后dispatch了RECEIVE_USER_DATA這個action表示接收到了網絡請求,并需要修改state(注:這里我們沒有考慮網絡請求失敗的情況),最后我們dispatch了FETCHING_DATA并告訴對應reducer下一個state的isFetchingData為false,表示數據拉取完畢。
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函數合并為一個reducer。
連接到React我們通過react-redux提供的connect方法與Provider來連接到React,Provider主要的作用是包裝我們的容器組件,connect用于將redux與react進行連接,connect() 允許你從 Redux store 中指定準確的 state 到你想要獲取的組件中。這讓你能獲取到任何級別顆粒度的數據,了解更多可以參考它的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í)行兩次的柯里化函數,第一次傳入的參數相當于一系列的定制化東西,第二次傳入的是你要連接的React組件,然后返回一個新的React組件。第一次執(zhí)行時傳入的參數是mapStateToProps, mapDispatchToProps, mergeProps, options。也就是說這里相當于幫組容器選擇它在整個Store中所需的state與dispatch回調,這些將會被connect以Props的形式綁定到App容器,我們可以通過React開發(fā)者工具看到這一點:
第一次執(zhí)行,選擇需要的state,第二次傳入App容器組件然后返回新的組件。然后創(chuà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用于網絡請求處理,最后window.devToolsExtension ? window.devToolsExtension() : f => f是為了連接我們的redux-dev-tool,可以明確的看到我們dispatch的action,還能達到時間旅行的效果。最后通過Provider輸入我們的store,整個應用就跑起來啦!
let mountRoot = document.getElementById("app"); ReactDOM.render(Run, mountRoot );
命令行輸入npm run dev,整個應用就跑起來了,在輸入框輸入Jiavan,我們來看看action與數據流:
在console面板,logger中間件為我們打印除了每一次dispatch action以及前后的state值,是不是非常直觀,然而厲害的還在后面。redux-dev-tool可以直接查看我們state tree以及對action做undo操作達到時間旅行的效果!
完整的demo在文章最后將會貼出,現(xiàn)在總結下:首先我們規(guī)劃了整個應用的state,然后進行數據流層的代碼開發(fā),同步異步action的編寫以及reducer的開發(fā),再通過選擇我們容器組件所需的state與dispatch回調通過connect方法綁定后輸出新的組件,通過創(chuàng)建store與Provider將其與React連接,這樣整個應用的任督二脈就被打通了。最后極力推薦Redux的官方文檔。
完整demo -> https://github.com/Jiavan/rea...
運行
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吧
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/80220.html
摘要:異步實現(xiàn)設計需要增加三種通知異步請求發(fā)起的異步請求成功的異步請求失敗的示例代碼如下返回參數完全可以自定義。這種分別在請求開始前,請求成功后,請求失敗后發(fā)送。表示數據的有效性,他的作用是在異步請求發(fā)送失敗后,告訴當前的數據是過時的數據。 說明:對Redux不了解的同學可先看看這篇文章Redux技術架構簡介(一) 前言 這里說的Redux異步實現(xiàn),是專指Redux中的異步Action實現(xiàn),...
摘要:舉例來說一個異步的請求場景,可以如下實現(xiàn)任何異步的邏輯都可以,如等等也可以使用的和。實際上在中,一個就是一個函數。 書籍完整目錄 3.4 redux 異步 showImg(https://segmentfault.com/img/bVyou8); 在大多數的前端業(yè)務場景中,需要和后端產生異步交互,在本節(jié)中,將詳細講解 redux 中的異步方案以及一些異步第三方組件,內容有: redu...
摘要:基本流程創(chuàng)建帶有三個方法發(fā)出處理數據每次后的數據即得到的數據即一個新的所以是一個對象每次都是上次返回的值所以用即返回的新狀態(tài)新的即本次的返回值所以每次都是往空的對象里先推再新增屬性或改變原來屬性的值層通過方法設置監(jiān)聽函數一旦發(fā)生變化就會 基本流程 1.創(chuàng)建store,帶有三個方法:store.dispatch,store.subscribe,store.getState import ...
摘要:在函數式編程中,異步操作修改全局變量等與函數外部環(huán)境發(fā)生的交互叫做副作用通常認為這些操作是邪惡骯臟的,并且也是導致的源頭。 注:這篇是17年1月的文章,搬運自本人 blog... https://github.com/BuptStEve/... 零、前言 在上一篇中介紹了 Redux 的各項基礎 api。接著一步一步地介紹如何與 React 進行結合,并從引入過程中遇到的各個痛點引出 ...
摘要:是官方文檔中用到的異步組件,實質就是一個中間件,簡單來說就是一個封裝表達式的函數,封裝的目的是延遲執(zhí)行表達式。這時我們需要對一般異步中間件進行處理。 曾經前端的革新是以Ajax的出現(xiàn)為分水嶺,現(xiàn)代應用中絕大部分頁面渲染會以異步流的方式進行。在Redux中,如果要發(fā)起異步請求,最合適的位置是在action creator中實現(xiàn)。但我們之前了解到的action都是同步情況,因此需要引入中間...
閱讀 3285·2021-11-24 09:39
閱讀 3866·2021-11-22 09:34
閱讀 4799·2021-08-11 11:17
閱讀 1060·2019-08-29 13:58
閱讀 2571·2019-08-28 18:18
閱讀 537·2019-08-26 12:24
閱讀 825·2019-08-26 12:14
閱讀 727·2019-08-26 11:58