初期參加工做開發項目時,使用React Native + Flux進行手機應用跨平臺開發,在上一篇博文中數據流架構學習筆記(一)-Flux 對Flux作了一次總結,本文是我對數據流管理架構學習總結的第二篇數據流架構學習筆記(二)-Redux,是我在工做過程當中對項目使用Redux進行重構狀態管理和數據流的學習記錄。javascript
2014年 Facebook 提出了 Flux 架構的概念和單向數據流管理的思想,並給出了管理狀態的基本數據流,可是隨着前端應用的複雜性指數級的提高,前端頁面須要管理的狀態也愈來愈多,因而出現了不少基於Flux基本的數據流概念和單向數據流思想的實現方式。2015年,Redux 出現,將 Flux 與函數式編程結合一塊兒,很短期內就成爲了最熱門的前端架構。html
在實際項目中,你應該有遇到過如下這樣狀況的發生:前端
Redux 把本身標榜爲一個「可預測的狀態容器 」,它充分利用函數式的特性,讓整個實現更加優雅純粹,使用起來也更簡單。java
Redux(oldState) => newState複製代碼
Redux 能夠看做是 Flux 的一次進化。Redux遵循如下三個基本原則:react
View 觸發數據更新 —> Actions 將數據傳遞到 Store —> Store 更新 state —> 更新 View。複製代碼
Redux 中整個應用的狀態存儲在一顆 object tree 中,對應一個惟一的 Store,而且 state 是隻讀的,使用純函數 reducer 來更新 state 會生成一個新的 state 而不是直接修改原來的。編程
Redux 經過以上約束讓 state 的變化可預測。redux
若是沒法理解這些概念,建議先學習Redux官方文檔,再來查看他人的博客和使用方式,才能更快的使用。promise
這裏以React Native實際項目登陸部分展現如何將Redux應用到React Native開發中進行數據管理,在實際架構項目時,每一個人有各自的編碼習慣,於是,雖然一樣是Redux,可是在各部分代碼寫法老是有所不同,而實際項目中用起來的寫法也是不同的,可是思想整體上是同樣的,不要拘泥於代碼的寫法,代碼只是做爲參考和總結,應該理解寫法的目的思想和如何體現Redux的融入和使用。bash
登陸頁:網絡
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import LoginAction from '../../actions/loginAction';
...
class Login extends Component {
...
_doLogin = () => {
const param = {
uid: this.state.uid,
pwd: this.state.pwd
};
this.props.actions.doLogin(param)
.then(() => {
const { navigation, login } = this.props;
if (login.status === 'done' && navigation) {
navigation.resetRouteTo('TabBar', { title: '首頁', selectedTab: 'home' });
} else {
Alert.alert(
'提示',
login.message
);
}
});
};
...
}
...
const mapStateToProps = (state) => {
return {
login: state.loginReducer
};
};
const mapDispatchToProps = dispatch => {
return ({
actions: bindActionCreators({ ...LoginAction }, dispatch)
});
};
export default connect(mapStateToProps, mapDispatchToProps)(Login);複製代碼
簡單說View層主要做用就是響應用戶的操做,而實際在代碼中咱們主要的做用就是觸發Action,如代碼中調用this.props.actions.doLogin()函數,在this.props會存在actions屬性是因爲在最後使用bindActionCreators方法將對應的LoginAction綁定至頁面組件Login中,這樣形成我在View層只會作調用action的操做,不會直接使用dispatch進行消息分發。這樣就完成了View -> Actions
的過程。
const _loginSuccess = (data) => {//eslint-disable-line
return {
type: ActionTypes.LOGIN_SUCCESS,
payload: {
user: data.uid
}
};
};
const _loginFailed = (error) => {
return {
type: ActionTypes.FAIL,
payload: {
message: error.message
}
};
};
const _doLogin = (url, param) => dispatch => {
dispatch(CommonAction.showLoading());
return Fetcher.postQsBodyFetch(url, param)
.then((response) => {
dispatch(CommonAction.dismissLoading());
dispatch(_loginSuccess(param, response));
}).catch((error) => {
dispatch(CommonAction.dismissLoading());
dispatch(_loginFailed(error));
});
};
const LoginAction = {
doLogin: (param) => _doLogin(NetLink.login, param),
loginSuccess: (data) => _loginSuccess(data),
loginFailed: (error) => _loginFailed(error),
};複製代碼
Action一般都是在進行網絡層調用、請求數據和分發數據,因在View層使用了bindActionCreators方法和組件綁定後,將會直接獲取View層組件dispatch屬性方法,使得在Action的純函數中在數據返回後調用dispatch()進行數據分發。這樣就完成了Actions -> Reducer
的過程。
import ActionType from '../constants/actionType';
const initialState = {
status: 'init',
user: '',
message: null
};
const loginReducer = (state = initialState, action) => {
switch (action.type) {
case ActionType.LOGIN_SUCCESS:
return Object.assign({}, state, {
status: 'done',
user: action.payload.user,
});
case ActionType.FAIL:
return Object.assign({}, state, {
status: 'fail',
message: action.payload.message,
});
default:
return state;
}
};複製代碼
Reducer相似原來Flux的store,做爲數據倉庫來源,這裏將會收到來自調用dipatch()後得到的消息,並進行處理和保存,並在及時更新數據後,經過redux的組件綁定,自動反饋至頁面組件中進行數據更新和異步渲染,而在這裏你應該return一個全新的對象,redux才能知道你是更新了當前組件關聯的reducer,到了這一步你應該會產生疑問,數據狀態是如何反饋至View,而你寫的普普統統的Action和Reducer等js文件是如何關聯上你的組件和應用。
入口組件Root.js:
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import Fetcher from './network/fetcher';
import Main from './containers/mainContainer';
import rootReducer from './reducers/rootReducer';
const middlewares = [thunk];
const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
const createLogger = require('redux-logger');
if (process.env.NODE_ENV === 'development') {
const logger = createLogger();
middlewares.push(logger);
}
function configureStore(initialState) {
const store = createStoreWithMiddleware(rootReducer, initialState);
return store;
}
const store = configureStore();
export default class Root extends Component {
constructor(props) {
super(props);
Fetcher.initNetworkState();
}
componentWillUnmount() {
Fetcher.removeNetworkStateListener();
}
render() {
return (
<Provider store={store}> <Main {...this.props} /> </Provider> ); } }複製代碼
其中rootReducer.js:
import { combineReducers } from 'redux';
import LoginReducer from './loginReducer';
...
const rootReducer = combineReducers({
loginReducer: LoginReducer,
...
});
export default rootReducer;複製代碼
先使用combineReducer將全部的reducer合併成一個rootReducer,使用rootReducer,在接着開始經過createStore方法建立了store對象,經過redux提供的Provider組件直接將store對象綁定至實際的View組件上,這樣就完成了
View 觸發數據更新 —> Actions 將數據傳遞到 Store —> Store 更新 state —> 更新 View。複製代碼
關於其中的applyMiddleware
,bindActionCreators
等等方法是關於異步actions、異步數據流、Middleware的進階知識。具體內容建議查看Redux官方文檔瞭解相關內容。如下Redux進階也將會大概講解他們的使用和爲何使用。
使用redux-thunk和redux-logger框架,並使用applyMiddleware和Middleware來增強Redux的使用,使Redux更增強大、規範和合理。其中redux-logger框架簡單理解就是添加一個Redux日誌打印和處理框架,開發者無需知道他如何規範打印出Redux的dispatch日誌的,可是在開發時,用於debug等是頗有用的工具。redux-thunk屬於處理異步Actions和異步數據流的框架。
什麼是異步Actions和異步數據流,簡單來講,就是網絡請求來控制的Actions。如App中你點擊一個按鈕當即發出了一個dispatch(), 這是你對App控制做出的dispatch(), 這叫作同步Actions,而異步Actions並非你控制的,如網絡請求成功或失敗後,纔會發出一個dispatch(),這就是異步Actions,你沒法知道這個dispatch()是什麼時間作出的操做,也不知道你發出的是成功的dispatch()或是失敗的dispatch()。
如我loginAction中方法,如今應該就能很好的理解這個方法這樣寫的原理:
const _doLogin = (url, param) => dispatch => {
dispatch(CommonAction.showLoading());
return Fetcher.postQsBodyFetch(url, param)
.then((response) => {
dispatch(CommonAction.dismissLoading());
dispatch(_loginSuccess(param, response));
}).catch((error) => {
dispatch(CommonAction.dismissLoading());
dispatch(_loginFailed(error));
});
};複製代碼
middleware翻譯成中文意思中間件,很貼切也很容易理解,像redux-thunk 或 redux-promise就能夠叫作中間件,若是你想使用這些中間件,就須要使用applyMiddleware等等相關方法爲你的項目添加上這些框架。使項目使用redux更增強大和規範。
像redux-thunk 或 redux-promise 這樣支持異步的 middleware 都包裝了 store 的 dispatch() 方法,以此來讓你 dispatch 一些除了 action 之外的其餘內容,例如:函數或者 Promise。你所使用的任何 middleware 均可以以本身的方式解析你 dispatch 的任何內容,並繼續傳遞 actions 給下一個 middleware。好比,支持 Promise 的 middleware 可以攔截 Promise,而後爲每一個 Promise 異步地 dispatch 一對 begin/end actions。
當 middleware 鏈中的最後一個 middleware 開始 dispatch action 時,這個 action 必須是一個普通對象。這是 同步式的 Redux 數據流 開始的地方(譯註:這裏應該是指,你可使用任意多異步的 middleware 去作你想作的事情,可是須要使用普通對象做爲最後一個被 dispatch 的 action ,來將處理流程帶回同步方式)。
middleware 能夠完成包括異步 API 調用在內的各類事情,瞭解它的演化過程是一件至關重要的事。而他們是如何演化過來的,並如何增強你的應用的,這裏再也不具體說明。
Redux很強大,也相對複雜。簡單的項目也許並不須要使用到,可是若是你的項目愈來愈大,數據愈來愈複雜,Redux將會使你項目更加規範和健壯。在項目中使用規範的框架架構,是一件很是重要的事情,你能夠爲你的項目使用架構,這是一件頗有趣的事情。
文章很長,Redux也很複雜,文中若有不對請告知,我會及時改正。一塊兒進步和學習。謝謝!