【原】redux異步操作學習筆記


摘要:

  發覺在學習react的生態鏈中,react+react-router+webpack+es6+fetch等等這些都基本搞懂的差不多了,可以應用到實戰當中,唯獨這個redux還不能,學習redux還學的挺久的。

  其中困擾我最久的就是redux的異步數據流的處理。難點主要是概念太多,接觸的詞太多,而且網上的案例看的頭都疼,很容易暈,已經暈了好多次了。后來被我簡化之后,終於搞懂了,哈哈。!來來來,今天總結一下,希望對大家有所幫助。不過本人主要是介紹redux的異步操作,如果對redux不是很熟悉的,可以看看我之前寫的一篇文章http://www.cnblogs.com/xianyulaodi/p/5399264.html

 

 

1、redux異步流程簡介

 

先來一張圖先,大概介紹一下redux的異步流程:

(圖片來自於百度圖片)

 

大概的流程是這樣的:

  首先發起一個action,然后通過中間件,這里為什么要用中間件呢,因為這樣dispatch的返回值才能是一個函數。通過store.dispatch,將狀態的的改變傳給store的小弟,reducer,reducer根據action的改變,傳遞新的狀態state。最后將所有的改變告訴給它的大哥,store。store保存着所有的數據,並將數據注入到組件的頂部,這樣組件就可以獲得它需要的數據了。

 

2、demo簡介

    這個demo是我根據官網的async改編的,其實也就是刪除了很多其他干擾的東西,讓它僅僅是獲得一個異步請求。初學嘛,能簡單則簡單,這樣比較明朗。

    官網原版的github地址: https://github.com/lewis617/react-redux-tutorial/tree/master/redux-examples/async

    我改編的github地址:https://github.com/xianyulaodi/reduxAsync

本文也是通過我自己改的來寫的。可以邊看代碼邊看此文,當然可能有些講的不是很對,歡迎指出

界面如下:

 

 界面很簡單,就是通過異步請求,將請求到的數據展現到界面上。可能你會說,不就是一個請求嘛,有啥大不了的。可是這個請求是走了一遍redux的流程的呢,哈哈。

 

3、代碼解析

 

下面對redux的異步流程做一個小小的解析。期間順便也回顧一下redux的一些基礎知識吧。

 

3.1 首先從action開始,代碼在  action/index.js

 1 import fetch from 'isomorphic-fetch'
 2 export const RECEIVE_POSTS = 'RECEIVE_POSTS'
 3 
 4 //獲取新聞成功的action
 5 function receivePosts(reddit, json) {
 6   return {
 7     type: RECEIVE_POSTS,
 8     reddit: reddit,
 9     posts: json.data.children.map(child =>child.data)
10   }
11 }
12 
13 function fetchPosts(subreddit) {
14 
15   return function (dispatch) {
16     
17     return fetch(`http://www.subreddit.com/r/${subreddit}.json`)
18       .then(response => response.json())
19       .then(json =>
20         dispatch(receivePosts(subreddit, json))
21       )
22   }
23 }
24 
25 //如果需要則開始獲取文章
26 export function fetchPostsIfNeeded(subreddit) {
27 
28   return (dispatch, getState) => {
29 
30       return dispatch(fetchPosts(subreddit))
31 
32     }
33 }

  此次的請求沒有用ajax,而是用了fetch,這個稍微了解一下即可,入門比較容易。如上面的redux異步流程圖一樣,它需要借助一個中間件 middleware。其實異步的精髓也就在這個中間件middleware這里了。搞懂了這個,可以說基本也就搞懂的redux的異步了。所以我會花比較大的篇幅去介紹這個中間件。

 

middleware有什么用?為什么要引入它呢?

 

在redux里,action僅僅是攜帶了數據的普通js對象( plain JavaScript objects)。action creator返回的值是這個action類型的對象。然后通過store.dispatch()進行分發……

action ---> dispatcher ---> reducers

 

如果遇到異步情況,比如點擊一個按鈕,希望2秒之后更新視圖,顯示消息“Hi”。我們可能這么寫ActionCreator:

var asyncSayActionCreator = function (message) {
    setTimeout(function () {
        return {
            type: 'SAY',
            message
        }
    }, 2000)
}

  這會報錯,因為這個asyncSayActionCreator返回的不是一個action,而是一個function。這個返回值無法被reducer識別。

也就是說,正常來說,action返回的是一個對象,而不是一個函數。如果返回函數,會出現錯誤。

  而異步操作呢,需要action的返回值是一個函數。那么咋辦呢,所以需要引入中間件middleware,它在中間起到了橋梁的作用,讓action的返回值可以是一個函數,從而傳到

reducer那里。也就是說,中間件是用在action發起之后,reducer接收到之前的這個時間段。

也可以這么說,Middleware 主要是負責改變Store中的dispatch方法,從而能處理不同類型的 action 輸入,得到最終的 Javascript Plain Object 形式的 action 對象。

  因此,上面那個ActionCreator就可以改寫為這樣:因為action的返回值是一個函數。

var asyncSayActionCreator = function (message) {
    return function (dispatch) {
        setTimeout(function () {
            dispatch({
                type: 'SAY',
                message
            })
        }, 2000)
    }
}

 

中間件是如何工作的

Middleware的中間件有很多,不過我的這個案例只引用了其中的一個,那就是redux-thunk

redux-thunk源碼如下:

export default function thunkMiddleware({ dispatch, getState }) {
  return next => action =>
    typeof action === 'function' ?
      action(dispatch, getState) :
      next(action);
}

   意思是如果action是一個函數,執行這個action函數,如果不是函數,執行next函數。

具體的原理可以看看這兩篇文章:

 Redux學習之一:何為middleware?

 
 
回到我的項目源代碼:
   fetchPostsIfNeeded這里就是一個中間件。redux-thunk會攔截fetchPostsIfNeeded這個action,會先發起數據請求,如果成功,就將數據傳給action.從而到達reducer那里。



再來看看reducer 目錄為 reducers/index.js

 1 import { combineReducers } from 'redux'
 2 import {
 3   RECEIVE_POSTS
 4 } from '../actions'
 5 
 6 
 7 function posts(state = {
 8   items: []
 9 }, action) {
10   switch (action.type) {
11 
12     case RECEIVE_POSTS:
13       // Object.assign是ES6的一個語法。合並對象,將對象合並為一個,前后相同的話,后者覆蓋強者。詳情可以看這里
14       //  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
15       return Object.assign({}, state, {
16         items: action.posts //數據都存在了這里
17       })
18     default:
19       return state
20   }
21 }
22 
23 //廢棄、接收到、開始接受新聞后,將state.postsByReddit設為相關參數
24 function postsByReddit(state = { }, action) {
25   switch (action.type) {
26     case RECEIVE_POSTS:
27       return Object.assign({}, state, {
28         [action.reddit]: posts(state[action.reddit], action)
29       })
30     default:
31       return state
32   }
33 }
34 
35 // 將所有的reducer結合為一個,傳給store
36 const rootReducer = combineReducers({
37   postsByReddit
38 })
39 
40 export default rootReducer
 
         

這個跟正常的reducer差不多。判斷action的類型,從而根據action的不同類型,返回不同的數據。這里將數據存儲在了items這里。這里的reducer只有一個。最后結合成rootReducer,傳給store。

 

store/configureStore.js

 

 1 import { createStore, applyMiddleware } from 'redux'
 2 import thunkMiddleware from 'redux-thunk'
 3 import createLogger from 'redux-logger'
 4 import rootReducer from '../reducers'
 5 
 6 const createStoreWithMiddleware = applyMiddleware(
 7   thunkMiddleware,  
 8   createLogger()  
 9 )(createStore)
10 
11 export default function configureStore(initialState) {
12   const store = createStoreWithMiddleware(rootReducer, initialState)
13 
14   if (module.hot) {
15     // Enable Webpack hot module replacement for reducers
16     module.hot.accept('../reducers', () => {
17       const nextRootReducer = require('../reducers')
18       store.replaceReducer(nextRootReducer)
19     })
20   }
21 
22   return store
23 }

我們是如何在 dispatch 機制中引入 Redux Thunk middleware 的呢?
我們使用了 applyMiddleware(),
const createStoreWithMiddleware = applyMiddleware(
  thunkMiddleware,
  createLogger()
)(createStore)
 
         

   其中,createLogger是一個很便捷的 middleware,用來打印 action 日志。通過使用指定的 middleware,action creator 除了返回 action 對象外還可以返回函數。

這時,這個 action creator 就成為了 thunk。

 

再來看看界面上的調用:在containers/App.js

部分代碼:

 

  //初始化渲染后觸發
  componentDidMount() {
    const { dispatch} = this.props
    // 這里可以傳兩個值,一個是 reactjs 一個是 frontend
    dispatch(fetchPostsIfNeeded('frontend'))
  }

 

改變狀態的時候也是需要通過dispatch來傳遞的。

數據的獲取是通過provider,將store里面的數據注入給組件。讓頂級組件提供給他們的子孫組件調用。代碼如下:

import 'babel-core/polyfill'
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './containers/App'
import configureStore from './store/configureStore'
const store = configureStore()
render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

 

     這樣就完成了redux的異步操作。其實最主要的區別還是action里面還有中間件的調用,其他的地方基本跟同步的redux差不多的。搞懂了中間件,就基本搞懂了redux的異步操

作。具體的大家還是需要看一下源碼和其他相應的文章介紹。當然,demo是不夠完善的,因為缺少請求發送失敗等等的處理。

再附上一張圖來結束今天的學習筆記:

 

 

今天的總結就到這里先,感覺自己總結的有點亂。剛好最近用react寫了一個小小的項目,下一步將異步和同步結合起來,真正用到實戰中,到時候再將這篇博客完善和補充。

由於是初學異步的操作,所以可能有些地方自己總結的錯了。有誤之處,歡迎指出。如果對於中間件還有很多困惑的同學,不妨看一下下面的鏈接。

 

參考:

 http://www.aliued.com/?p=3204

 http://www.cnblogs.com/bingooo/p/5500108.html#top

 http://cn.redux.js.org/

 https://segmentfault.com/a/1190000003746223

 https://zhuanlan.zhihu.com/p/20597452?utm_source=tuicool&utm_medium=referral 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM