在以前的淺談Flux架構及Redux實踐一文中咱們初步的談及了Redux的數據流思想,並作了一個簡單的加減器。可是尚未接觸到Redux更多經常使用的場景,異步操做、API調用,如何鏈接到UI層等,Redux能夠與不少框架搭配包括Vue、React甚至是純JavaScript。後面咱們會用一個實例--經過github API獲取我的信息,來將Redux middleware、async action、鏈接到React貫穿其中。先看看咱們最後寫的demo的樣子。javascript
依然先看看Redux做者Dam的描述:css
It provides a third-party extension point between dispatching an
action, and the moment it reaches the reducer.
個人理解是,middleware提供了一個你能夠修改action的機制,這和Express/Koa的中間件有些相似,只不過這裏的中間件主要是操做action。中間件對異步的action實現很是重要,由於在以前的文章中咱們談到,action是一個行爲抽象,只是一個對象,reducer是一個純函數,不該該有API調用和反作用的操做。那麼怎麼解決異步的問題?咱們確定不能在reducer中寫,那麼就考慮到了action -> reducer這個過程,這就是redux middleware:html
action -> middleware modify action -> reducer
它提供的是位於 action 被髮起以後,到達 reducer 以前的擴展點。 你能夠利用 Redux middleware 來進行日誌記錄、建立崩潰報告、調用異步接口或者路由等等。java
在上一篇文章中咱們使用的同步action,action creator返回的是一個對象,可是異步action能夠是一個函數,雖然函數也是對象,這裏咱們只是爲了區分兩種不一樣的狀況。經過使用指定的 middleware,action creator能夠返回函數。這時,這個 action creator 就成爲了 thunk。當 action creator 返回函數時,這個函數會被 Redux Thunk middleware 執行。這個函數並不須要保持純淨,它還能夠帶有反作用,包括執行異步 API 請求。這個函數還能夠 dispatch action,就像 dispatch 前面定義的同步 action 同樣。那麼如何在action中進行網絡請求?標準的作法是使用 Redux Thunk middleware。要引入 redux-thunk
這個專門的庫才能使用。node
咱們將採用ES6語法,webpack進行打包,webpack-dev-server啓一個本地服務器,而後用HMR技術進行React熱加載,看看webpack配置信息:react
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
來進行熱更新。webpack
再看看咱們最後的目錄結構:git
├── 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
。github
state
使用Redux很是重要的一點就是設計好頂層的state,在demo中咱們須要的state大概長這個樣子:web
{ isFetchingData, // boolean username, // string profile, // object }
其中isFetchingData
是網絡請求的狀態,正在拉取數據爲true,username
是咱們要獲取用戶信息的名字,profile
是咱們拉取用戶的詳細信息,這個將會是一個Ajax請求,最後由github API提供。
actions
同步action咱們再也不講述,上一篇文章已經說得比較清楚,這裏咱們重點說異步action,app的全部action以下:
export const GET_INFO = 'GET_INFO'; // 獲取用戶信息 export const FETCHING_DATA = 'FETCHING_DATA'; // 拉取狀態 export const RECEIVE_USER_DATA = 'RECEIVE_USER_DATA'; //接收到拉取的狀態 // 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,還比較新可使用社區提供的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-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 ( <div className='container'> <Search fetchUserInfo={fetchUserInfo} isFetchingData={isFetchingData} /> {'name' in profile ? <Profile profile={profile} isFetchingData={isFetchingData} /> : ''} </div> ); } } export default connect( mapStateToProps, mapDispatchToProps )(App);
connect
是個能夠執行兩次的柯里化函數,第一次傳入的參數至關於一系列的定製化東西,第二次傳入的是你要鏈接的React組件,而後返回一個新的React組件。第一次執行時傳入的參數是mapStateToProps, mapDispatchToProps, mergeProps, options。也就是說這裏至關於幫組容器選擇它在整個Store中所需的state與dispatch回調,這些將會被connect以Props的形式綁定到App容器,咱們能夠經過React開發者工具看到這一點:
第一次執行,選擇須要的state,第二次傳入App容器組件而後返回新的組件。而後建立整個應用的store:
const loggerMiddleware = createLogger(); const store = createStore( rootReducer, compose( applyMiddleware( thunkMiddleware, loggerMiddleware, ), window.devToolsExtension ? window.devToolsExtension() : f => f ) );
這裏咱們用到了兩個中間件,loggerMiddleware
用於輸出咱們每一次的action,能夠明確的看到每次action後state的先後狀態,thunkMiddleware
用於網絡請求處理,最後window.devToolsExtension ? window.devToolsExtension() : f => f
是爲了鏈接咱們的redux-dev-tool,能夠明確的看到咱們dispatch的action,還能達到時間旅行的效果。最後經過Provider
輸入咱們的store,整個應用就跑起來啦!
let mountRoot = document.getElementById('app'); ReactDOM.render( <Provider store={store}> <App /> </Provider>, mountRoot );
命令行輸入npm run dev
,整個應用就跑起來了,在輸入框輸入Jiavan,咱們來看看action與數據流:
在console面板,logger中間件爲咱們打印除了每一次dispatch action以及先後的state值,是否是很是直觀,然而厲害的還在後面。redux-dev-tool能夠直接查看咱們state tree以及對action作undo操做達到時間旅行的效果!
完整的demo在文章最後將會貼出,如今總結下:首先咱們規劃了整個應用的state,而後進行數據流層的代碼開發,同步異步action的編寫以及reducer的開發,再經過選擇咱們容器組件所需的state與dispatch回調經過connect方法綁定後輸出新的組件,經過建立store與Provider將其與React鏈接,這樣整個應用的任督二脈就被打通了。最後極力推薦Redux的官方文檔。
完整demo -> https://github.com/Jiavan/rea...
運行
1. npm install 2. webpack 3. npm run dev
原文出處 https://github.com/Jiavan/jia... 以爲對你有幫助就給個star吧