[React Native]Redux的基本使用方式

好久不寫文章了抖棘,這段時間斷斷續(xù)續(xù)在學習Redux。Redux對于新手狸涌,尤其我這樣一個之前從未做過WEB開發(fā)切省,也不知何為Flux,確實不太好理解帕胆。所以朝捆,我準備用一個簡單的示例,來演示如何編寫一個基于Redux的程序懒豹。

關于Redux的前世今生芙盘,不是本文介紹的重點。建議讀者在有一定Redux認知的基礎上來閱讀本篇文章脸秽,不然可能看的還是云里霧里儒老,這里推薦幾個介紹Redux的文章:

這里,以一個簡單的登錄功能雕沿,來介紹Redux练湿。要實現(xiàn)的效果很簡單:


preview.gif

點擊登錄,模擬一個用戶登錄(實際是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)交還給storestore會通知視圖刷新。所以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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市骡技,隨后出現(xiàn)的幾起案子鸣个,更是在濱河造成了極大的恐慌,老刑警劉巖布朦,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件囤萤,死亡現(xiàn)場離奇詭異,居然都是意外死亡是趴,警方通過查閱死者的電腦和手機涛舍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唆途,“玉大人富雅,你說我怎么就攤上這事「匕幔” “怎么了没佑?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長温赔。 經(jīng)常有香客問我蛤奢,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任远剩,我火速辦了婚禮扣溺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瓜晤。我一直安慰自己锥余,他們只是感情好,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布痢掠。 她就那樣靜靜地躺著驱犹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪足画。 梳的紋絲不亂的頭發(fā)上雄驹,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天,我揣著相機與錄音淹辞,去河邊找鬼医舆。 笑死,一個胖子當著我的面吹牛象缀,可吹牛的內(nèi)容都是我干的蔬将。 我是一名探鬼主播,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼央星,長吁一口氣:“原來是場噩夢啊……” “哼霞怀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起莉给,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤毙石,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后颓遏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體徐矩,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年叁幢,在試婚紗的時候發(fā)現(xiàn)自己被綠了滤灯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡遥皂,死狀恐怖力喷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情演训,我是刑警寧澤弟孟,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站样悟,受9級特大地震影響拂募,放射性物質(zhì)發(fā)生泄漏庭猩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一陈症、第九天 我趴在偏房一處隱蔽的房頂上張望蔼水。 院中可真熱鬧,春花似錦录肯、人聲如沸趴腋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽优炬。三九已至,卻和暖如春厅贪,著一層夾襖步出監(jiān)牢的瞬間蠢护,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工养涮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留葵硕,地道東北人。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓贯吓,卻偏偏與公主長得像懈凹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子宣决,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內(nèi)容