好久不寫文章了抖棘,這段時間斷斷續(xù)續(xù)在學習Redux。Redux對于新手狸涌,尤其我這樣一個之前從未做過WEB開發(fā)切省,也不知何為Flux,確實不太好理解帕胆。所以朝捆,我準備用一個簡單的示例,來演示如何編寫一個基于Redux的程序懒豹。
關于Redux的前世今生芙盘,不是本文介紹的重點。建議讀者在有一定Redux認知的基礎上來閱讀本篇文章脸秽,不然可能看的還是云里霧里儒老,這里推薦幾個介紹Redux的文章:
- 看漫畫,學 Redux-不寫一行代碼记餐,輕松看懂 Redux 原理驮樊。
- Redux 中文文檔-官方文檔,必看片酝!
- 在react-native中使用redux-簡單示例囚衔,有源碼~
這里,以一個簡單的登錄功能雕沿,來介紹Redux练湿。要實現(xiàn)的效果很簡單:
點擊登錄,模擬一個用戶登錄(實際是fetch
一個網(wǎng)址)审轮,成功之后攜帶用戶信息跳轉(zhuǎn)到一個新的頁面并展示肥哎。
Redux三個最重要的概念:action,reducer疾渣,store贤姆。我們一步步看實例如何實現(xiàn)。
action
'use strict';
import * as types from '../constants/ActionTypes';
// 模擬服務器返回的用戶信息
let user = {
'name': 'admin',
'age': '24'
}
// 執(zhí)行登錄
export function doLogin() {
return dispatch = >{
dispatch(isLogining());
// 模擬用戶登錄
let result = fetch('https://github.com/').then((res) = >{
dispatch(loginSuccess(true, user));
}).
catch((e) = >{
dispatch(loginSuccess(false, null));
});
}
}
// 正在登錄
function isLogining() {
return {
type: types.LOGIN_IN_DOING
}
}
// 登錄完成
function loginSuccess(isSuccess, user) {
return {
type: types.LOGIN_IN_DONE,
isSuccess: isSuccess,
user: user
}
}
actions/Login.js
定義了store的行為:doLogin()
- 首先稳衬,
dispatch(isLogining());
發(fā)出一個action
霞捡,表示正在登錄。 - 然后薄疚,使用
fetch
訪問一個網(wǎng)址(模擬登錄過程)碧信,成功之后使用dispatch(loginSuccess(true, user));
發(fā)出一個action赊琳,表示登錄成功,并且把模擬的用戶數(shù)據(jù)發(fā)出去砰碴;當然躏筏,如果失敗,也會使用dispatch(loginSuccess(false, null));
呈枉,只不過數(shù)據(jù)為空趁尼。
有了action,接下來需要對應的reducer來處理了猖辫。
reducer
'use strict';
import * as types from '../constants/ActionTypes';
// 初始狀態(tài)
const initialState = {
status: 'init', // init,doing,done
isSuccess: false,
user: null,
}
export default function loginIn(state = initialState, action) {
switch (action.type) {
case types.LOGIN_IN_INIT: // 初始狀態(tài)
return Object.assign({}, state, {
status: 'init',
isSuccess: false,
user: null
});
case types.LOGIN_IN_DOING: // 正在登錄
return Object.assign({}, state, {
status: 'doing',
isSuccess: false,
user: null
});
case types.LOGIN_IN_DONE: // 登錄完成
return Object.assign({}, state, {
status: 'done',
isSuccess: action.isSuccess,
user: action.user
})
default:
return state;
}
}
reducers/Login.js
中:loginIn
其實就是對action的處理酥泞,負責返回新的狀態(tài)的函數(shù),這也是reducer的存在的作用啃憎。
由于Redux中只允許有一個store芝囤,當業(yè)務越來越龐大的時候,我們就需要拆分出N個reducer辛萍。這時候悯姊,就需要把這N個reducer組合起來,因此我們需要一個根reducer贩毕。
reducers/Index.js
:
'use strict';
import {combineReducers} from 'redux';
import loginIn from './Login';
const rootReducer = combineReducers({
loginIn
});
export default rootReducer;
- combineReducers是將所有的reducer進行組合悯许,因為我們可能有N個reducer。
store
'use strict';
import {createStore, applyMiddleware} from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers/Index';
const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
export default function configureStore(initialState) {
const store = createStoreWithMiddleware(rootReducer, initialState);
return store;
}
這是store的一個基本寫法
-
applyMiddleware
表示將中間件(thunkMiddleware:異步中間件等)應用在redux action過程中辉阶。 -
createStoreWithMiddleware
表示使用reducer來創(chuàng)建store岸晦。
程序入口
import React, { Component } from 'react';
import {Provider} from 'react-redux';
import configureStore from './store/ConfigureStore';
import App from './containers/App';
const store = configureStore();
export default class Root extends Component {
render() {
return (
<Provider store={store}>
<App />
</Provider>
);
}
}
- 使用
Provider
來包裹整個程序的入口組件App
,同時將store
傳進去睛藻。 - 實際入口組件是
App
启上,讓我們來看下
containers/App.js
:
import React, { Component } from 'react';
import {
View,
Text,
Navigator
} from 'react-native';
import LoginPage from '../pages/LoginPage'
export default class App extends Component {
render() {
return (
<Navigator
style={{flex: 1}}
initialRoute= {{id: 'LoginPage', component: LoginPage}}
configureScene= {this.configureScene}
renderScene= {this.renderScene}
/>
);
}
configureScene(route, routeStack) {
if (route.sceneConfig) { // 有設置場景
return route.sceneConfig;
}
return Navigator.SceneConfigs.PushFromRight; // 默認,右側(cè)彈出
}
renderScene(route, navigator) {
return <route.component {...route.passProps} navigator= {navigator}/>;
}
}
不熟悉Navigator的朋友店印,可以先去閱讀這篇文章[React Native]導航器Navigator冈在。
- 導航控制器的根頁面是
LoginPage
,頁面內(nèi)容很簡單按摘,如下pages/LoginPage.js
包券。
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import {connect} from 'react-redux';
import {doLogin} from '../actions/Login'
import MainPage from '../pages/MainPage'
class LoginPage extends Component {
shouldComponentUpdate(nextProps, nextState)
{
// 登錄完成,且成功登錄
if (nextProps.status === 'done' && nextProps.isSuccess) {
this.props.navigator.replace({
id: 'MainPage',
component: MainPage,
passProps: {
user: nextProps.user
},
});
return false;
}
return true;
}
render() {
let tips;
if (this.props.status === 'init')
{
tips = '請點擊登錄';
}
else if (this.props.status === 'doing')
{
tips = '正在登錄...';
}
else if (this.props.status === 'done' && !this.props.isSuccess)
{
tips = '登錄失敗, 請重新登錄';
}
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center', flexDirection: 'column'}}>
<Text>{tips}</Text>
<TouchableOpacity style={{backgroundColor: '#FF0000'}} onPress={this.handleLogin.bind(this)}>
<View style={{flexDirection: 'row', alignItems: 'center', justifyContent: 'center', width: 100, height: 50}}>
<Text style={{color: '#FFFFFF', fontSize: 20}}>登錄</Text>
</View>
</TouchableOpacity>
</View>
);
}
// 執(zhí)行登錄
handleLogin()
{
this.props.dispatch(doLogin());
}
}
function select(store)
{
return {
status: store.loginIn.status,
isSuccess: store.loginIn.isSuccess,
user: store.loginIn.user
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default connect(select)(LoginPage);
-
connect(select)(LoginPage)
表示LoginPage
組件對store
的狀態(tài)感興趣炫贤。 -
select
函數(shù)的作用是將store
的狀態(tài)綁定到當前組件的props
中溅固。 -
handleLogin()
執(zhí)行登錄,使用this.props.dispatch(doLogin())
觸發(fā)action
兰珍,經(jīng)過reducer
處理后侍郭,新的狀態(tài)交還給store
,store
會通知視圖刷新。所以shouldComponentUpdate
會被調(diào)用亮元,然后猛计,判斷登錄成功則切換頁面到MainPage
(并攜帶參數(shù)user
)。 -
MainPage
比較簡單爆捞,僅僅展示了user
的內(nèi)容奉瘤,這里不再貼代碼了。
至此煮甥,一個簡單的Redux示例就完成了盗温,讓我們來稍微總結(jié)下:
- 整個應用只有一個
store
,用來保存所有的狀態(tài)成肘,視圖不需要自己維護狀態(tài)卖局。 - 視圖通過
connect
函數(shù)綁定到store
,當store
狀態(tài)變化后艇劫,store
會通知視圖刷新吼驶。 - 觸發(fā)一個
action
之后惩激,會經(jīng)過可能N個reducers
處理店煞,最后根reducer
會將所有reducers
處理之后的狀態(tài)合并,然后交給store
风钻,store
再通知視圖刷新顷蟀。
本文的源碼地址:Demo12