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

好久不写文章了,这段时间断断续续在学习Redux。Redux对于新手,尤其我这样一个之前从未做过WEB开发,也不知何为Flux,确实不太好理解。所以,我准备用一个简单的示例,来演示如何编写一个基于Redux的程序。

关于Redux的前世今生,不是本文介绍的重点。建议读者在有一定Redux认知的基础上来阅读本篇文章,不然可能看的还是云里雾里,这里推荐几个介绍Redux的文章:

这里,以一个简单的登录功能,来介绍Redux。要实现的效果很简单:


preview.gif

点击登录,模拟一个用户登录(实际是fetch一个网址),成功之后携带用户信息跳转到一个新的页面并展示。

Redux三个最重要的概念:action,reducer,store。我们一步步看实例如何实现。

action

'use strict';
import * as types from '../constants/ActionTypes';

// 模拟服务器返回的用户信息
let user = {
    'name': 'admin',
    'age': '24'
}

// 执行登录
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());发出一个action,表示正在登录。
  • 然后,使用fetch访问一个网址(模拟登录过程),成功之后使用dispatch(loginSuccess(true, user));发出一个action,表示登录成功,并且把模拟的用户数据发出去;当然,如果失败,也会使用dispatch(loginSuccess(false, null));,只不过数据为空。

有了action,接下来需要对应的reducer来处理了。

reducer

'use strict';

import * as types from '../constants/ActionTypes';

// 初始状态
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: // 初始状态
      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的处理,负责返回新的状态的函数,这也是reducer的存在的作用。

由于Redux中只允许有一个store,当业务越来越庞大的时候,我们就需要拆分出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来创建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; // 默认,右侧弹出
  }
  renderScene(route, navigator) {
    return <route.component {...route.passProps} navigator= {navigator}/>;
  }
}

不熟悉Navigator的朋友,可以先去阅读这篇文章[React Native]导航器Navigator

  • 导航控制器的根页面是LoginPage,页面内容很简单,如下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>
    );
  }

  // 执行登录
  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的状态感兴趣。
  • select函数的作用是将store的状态绑定到当前组件的props中。
  • handleLogin()执行登录,使用this.props.dispatch(doLogin())触发action,经过reducer处理后,新的状态交还给storestore会通知视图刷新。所以shouldComponentUpdate会被调用,然后,判断登录成功则切换页面到MainPage(并携带参数user)。
  • MainPage比较简单,仅仅展示了user的内容,这里不再贴代码了。

至此,一个简单的Redux示例就完成了,让我们来稍微总结下:

  • 整个应用只有一个store,用来保存所有的状态,视图不需要自己维护状态。
  • 视图通过connect函数绑定到store,当store状态变化后,store会通知视图刷新。
  • 触发一个action之后,会经过可能N个reducers处理,最后根reducer会将所有reducers处理之后的状态合并,然后交给storestore再通知视图刷新。

本文的源码地址Demo12

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容