asynchronous action creator 做數(shù)據(jù)取回纠炮,至少發(fā)送3個(gè)不同的actions:
- an action 查詢store, 請求開始(request began)
- an action 查詢store, 請求成功的完成(request finished successfully)
- an action 查詢store, 請求失敗(request failed)
下面以pro react中的示例作介紹,這個(gè)項(xiàng)目主要是查詢飛機(jī)航班信息和飛機(jī)票信息了解具體操作流程:
1.目錄結(jié)構(gòu)
-
app/
- actions/
- api/
- components/
- reducers/
- store/
- index.js
- constants.js
其中 粗體帶/
表示文件夾雨饺, 斜體
表示文件, app/ 文件夾在根目錄下。
2.定義constants.js
這個(gè)常量文件主要是項(xiàng)目中發(fā)送請求的類型,通常都是一邊開發(fā),一邊根據(jù)需求來實(shí)現(xiàn)的牵敷,這里由于是示例,暫且先一次性寫出來:
// constants.js
/*
* REQUEST_AIRPORTS: 表示應(yīng)用開始取回航班信息
* RECEIVE_AIRPORTS: 通過負(fù)載(preloaders)來表示異步取回信息失敗或者成功(注意這個(gè)動(dòng)作表示2種狀態(tài))
* CHOOSE_AIRPORT : 選擇起點(diǎn)和終點(diǎn)的操作法希,同步操作
* REQUEST_TICKETS : 表示應(yīng)用請求票務(wù)信息
* RECEIVE_TICKETS : 異步取回票務(wù)信息成功或失敗(注意這個(gè)動(dòng)作表示2種狀態(tài)枷餐,成功或失敗)
*/
export default const REQUEST_AIRPORTS = 'request airports';
export default const RECEIVE_AIRPORTS = 'receive airports';
export default const CHOOSE_AIRPORT = 'choose airport';
export default const REQUEST_TICKETS = 'request tickets';
export default const RECEIVE_TICKETS = 'receive tickets';
3.創(chuàng)建輔助API函數(shù),用來獲取服務(wù)器中的數(shù)據(jù)
這些數(shù)據(jù)APIs現(xiàn)在都由示例提供苫亦,通過上面的示例我們知道有2次異步的操作: 取回航班信息和取回票務(wù)信息毛肋。
本例使用 fetch
進(jìn)行ajax操作,返回一個(gè)promise,在 api/文件夾下:
// api/AirCheapAPI.js
import 'whatwg-fetch';
// 用fetch取回?cái)?shù)據(jù)
// 對數(shù)據(jù)進(jìn)行json()格式化
// 整個(gè)函數(shù)返回一個(gè)promise, 等待進(jìn)一步操作
const AirCheapAPI = {
// 取回航班信息
fetchAirports() {
return fetch('https://aircheapapi.pro-react.com/airports')
.then(response => response.json());
}屋剑,
// 取回票務(wù)信息
fetchTickets(origin, destination) {
return fetch(`https://aircheapapi.pro-react.com/tickets?origin=${origin}&destination=${destination}`)
.then(response => response.json());
}
}
export default AirCheapAPI;
4.AirportActionCreators
根據(jù)常量中的動(dòng)作類型和需求來定義actions, 再將其寫成函數(shù)的形式润匙,方便傳入到數(shù)據(jù)中,在實(shí)際的開發(fā)中,這些不可能一蹴而就唉匾,往往都是按需開發(fā)孕讳。
// actions/AirportActionCreators.js
import {
REQUEST_AIRPORTS,
RECEIVE_AIRPORTS,
CHOOSE_AIRPORT,
REQUEST_TICKETS,
RECEIVE_TICKETS
} from '../constants';
import AirCheapAPI from '../api/AirCheapAPI';
const AirportActionCreators = {
// 獲取航班信息
// 異步操作, 返回一個(gè)函數(shù)
// Thunk action creator
fetchAirportsInfo(origin, destination) {
return (dispatch) => {
// 本地發(fā)送請求航班信息
dispatch({ type: REQUEST_AIRPORTS });
// promise
// 取回?cái)?shù)據(jù)成功(success), 則發(fā)送RECEIVE_AIRPORTS動(dòng)作和preload
// success, airports為負(fù)載
// 取回?cái)?shù)據(jù)失敗(error), 則發(fā)送RECEIVE_AIRPORTS動(dòng)作和preload
AirCheapAPI.fetchAirports().then(
(success) => dispatch({ type: RECEIVE_AIRPORTS, success: true, airports }),
(error) => dispatch({ type: RECEIVE_AIRPORTS, success: false })
)
};
},
// 選擇出發(fā)地和目的地
// 同步操作巍膘, 返回一個(gè)action 對象
// target厂财, code 都是發(fā)送動(dòng)作時(shí)的負(fù)載
chooseAirport(target, airport) {
return {
type: CHOOSE_AIRPORT,
target,
code: airport ? airport.value : ''
}
},
// 獲取票務(wù)信息
// 異步操作, 返回一個(gè)函數(shù)
fetchTicketsInfo(origin, destination) {
return (dispatch) => {
dispatch({ type: REQUEST_TICKETS });
AirCheapAPI.fetchTickets(origin, destination).then(
(success) => dispatch({ type: RECEIVE_TICKETS, success: true, tickets}),
(error) => dispatch({ type: RECEIVE_TICKETS, success: false })
)
};
}
}
export default AirportActionCreators;
通過上面可以看出峡懈,異步操作一般調(diào)用返回一個(gè)函數(shù)璃饱,調(diào)用輔助api,而同步操作肪康,返回一個(gè)action對象即可
4.reducers
寫完actionCreators之后就該寫reducers(通常需要知道數(shù)據(jù)結(jié)構(gòu)),一般將大的reducers工具功能劃分成小的單個(gè)reducer荚恶,最后再合并成一個(gè)rootReducer。
更具constants.js可以知道總共有5種類型的action type磷支, 但其中 REQUEST_AIRPORTS
和REQUEST_TICKETS
在異步操作中完成谒撼。
airports.js:
// reducers/airports.js
import { RECEVIE_AIRPORTS } from '../constants';
const airports = (state = [], action) => {
switch (action.type) {
case RECEVIE_AIRPORTS:
return action.tickets;
default:
return state;
}
}
export default airports;
route.js:
// reducers/route.js
import update from 'react-addons-update';
import { CHOOSE_AIRPORT } from '../constants';
const initialState = {
origin: '',
destination: ''
};
const route = (state = initialState, action) => {
switch (action.type) {
case CHOOSE_AIRPORT:
// 更新狀態(tài)樹中的route狀態(tài)
// action.target 表示 'origin'或者'destination'
return update(state, { [action.target]: { $set: action.code } });
default:
return state;
}
}
export default route;
tickets.js:
// reducers/tickets.js
import { REQUEST_TICKETS, RECEVIE_TICKETS } from '../constants';
const tickets = (state = [], action) => {
switch (action.type) {
// 這里的REQUEST_TICKETS主要用于清空狀態(tài)樹中tickets的內(nèi)容
case REQUEST_TICKETS:
return [];
case RECEVIE_TICKETS:
return action.tickets;
default:
return state;
}
}
export default tickets;
將多個(gè)reducers合并成一個(gè)rootReducer, index.js:
// reducers/index.js
import { combineReducers } from 'redux';
import airports from './airports';
import route from './route';
import tickets from './tickets';
const rootReducer = combineReducers({
airports,
route,
tickets
});
export default rootReducer;
5.store
完成reducers之后,下一步就是寫store了,因?yàn)楫惒降脑蛭肀罚枰砑又虚g件react-thunk,另外這里自己寫一個(gè)logger的中間件廓潜。
// store/index.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'react-thunk';
import rootReducer from '../reducers/index';
// 自定義logger中間件
const logger = (store) => (next) => (action) => {
if (typeof action !== 'function') {
console.log('dispatching:', action)
}
next(action);
};
const aircheapStore = createStore(
rootReducer,
applyMiddleware(logger, thunk)
);
export default aircheapStore;
如果配合redux-devtools chrome插件,可以寫為:
import { createStore, applyMiddleware, compose } from 'redux';
// ...
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const aircheapStore = createStore(
rootReducer,
composeEnhancers(applyMiddleware(logger, thunk))
);
export default aircheapStore;
到目前為止,所有的基本框架已經(jīng)搭建好了茉帅,剩下的就是去寫components/中的組件了,當(dāng)然這只是示例锭弊,實(shí)際開發(fā)中肯定要先寫組件堪澎,然后依據(jù)需求添加上面的api, constants, actions, reducers, store。
總結(jié)
寫這個(gè)主要是為了熟悉開發(fā)流程味滞,以及異步操作的處理當(dāng)中要注意的一些事項(xiàng)和規(guī)范樱蛤。真實(shí)項(xiàng)目目前做的比較少,大致流程可以理解為:
- 寫設(shè)計(jì)組件結(jié)構(gòu)剑鞍,由幾部分組件昨凡,寫出presentation components(視覺組件)
- 然后初步列舉出所需要的功能,將功能的實(shí)現(xiàn)以動(dòng)作的形式命名蚁署,寫入到constants.js中
- 列舉出可能會(huì)出項(xiàng)的異步操作便脊,寫入到輔助api中
- 根據(jù)動(dòng)作類型寫出actionCreator, 及對應(yīng)的reducer
- 將routeReducer寫入store
- 再寫container components(容器組件), 然后調(diào)用store中的所需要的狀態(tài)
當(dāng)然狀態(tài)樹(state tree)這一點(diǎn)上對于我這樣的新手來說十分的困難,如果設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)這又是一個(gè)大難題了光戈。
2016/12/12 17:17:49