ReactNative引用Redux框架

Redux是受到了Facebook Flux和Elm启发的应用构架。Redux使用了类似于Flux的单向数据流,但是它只有一个单一的store对象,这个store对象通过克隆原始的store来改变,它调用reducer将action和之前的state作为参数,reducer并不产生副作用。Redux中没有Dispatcher。

Redux 和传统 Flux 框架的比较

网上有很多关于flux与redux的框架图,我个人感觉这两张最为直接易懂,所以就借用过来,在此对原图的作者表示感谢。


Flux框架图
Redux框架图

不像Flux,在Redux中有一个单一的store对象,包含整个应用程序的state。这个store是由对象树结构组成的,它是不变的。每次state需要改变的时候,一个新的对象树就创造了出来,合并了先前state中的数据和改变的数据。当一个action对象被分派到store中的时候,改变就被触发。action是一个简单的对象,其中包含了需要执行的操作的类型以及一些负载。改变由reducers 来执行,reducers 是没有副作用的纯函数,将先前的state和一个action作为参数。它们会返回由应用action产生的新的state。

Store不是一个类,而是一个伴随着一些方法的对象。通过在应用程序的最初的state执行root reducer可以创造出store。为了扩展应用程序,我们需要添加附加的reducers。每个reducer都维护一个state树的一支。Redux提供了一个方法,可以将reducers合并成一个,当store被创造出来的时候,它可以做一个简单的调用。

不像Flux一样,在Redux中没有主要的Dispatcher。当一个action需要被执行时,store的dispatch()方法被调用,将action当作参数。然后所有的监听器被通知state已经改变了,它们可以选择去获取新的state,然后相应地呈现相关组成部分。

Redux的三原则

1. Single source of truth单一数据源,数据流向也是单一方向。整个应用的state,存储在唯一一个javascript对象中,同时也只有一个store用于存储这个对象.
2. State is read-only状态是只读的。唯一能改变state的方法,就是触发action操作。action是用来描述正在发生的事件的一个对象。
** 3. Changes are made with pure functions**在改变state tree时,用到action,同时也需要编写对应的reducers才能完成state改变操作。

Redux扮演的角色

我们都知道MVC的设计模式,它的业务逻辑、数据、界面显示分离的方法给我们带来的好处不言而喻。如果用MVC模式去看待React和Redux的话,React承担的就是MVC中的View的角色,而Redux框架给我的感觉是扮演MVC中的model和controller,它负责接收View的交互事件,然后将处理完成后的结果返回给View,View根据结果重新刷新渲染。

这样做的好处是开发者只需要专心实现View,业务逻辑和数据从View中剥离出来,使项目结构分层清晰,代码职责均衡,降低视图、数据、业务逻辑之间的耦合度。整个数据的流向是单一的,使结果是可预测的。

ReactNative项目Redux框架的使用

安装依赖包

. redux

. react-redux

. redux-thunk(一个异步的中间件实现库)

. redux-persist(redux-persist是Redux的持久化的实现,可根据项目需求来确定要不要安装)

为了我们的 app 能在没有网络或者网络条件不好的情况下工作,我们需要离线的本地存储。现代的应用包括 SPA(单页面应用, Single Page Application) ,原生 App 都对状态持久化有强烈的需求,浏览器提供了 LocalStorage 、IndexedDB 等持久化方案和标准,React Native 提供了 AsyncStorage 都是用来解决这些问题。

在项目的根目录下使用npm install命令安装依赖包:

$ npm install packagename --save

Redux模块的编写

根目录下多出来的部分

. Action

store数据的唯一来源,如果我们想修改store中的数据,触发Action是唯一方法,它包含一个类型以及相关数据,通过 Store 的 dispatch() 函数发送到 Store。

//types.js

export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

//actions.js

import * as types from './types';
export function increment() {
  return {
    type: types.INCREMENT
  };
}

export function decrement() {
  return {
    type: types.DECREMENT
  };
}

. Reducer

reducer是一个纯函数,Action只是用来描述事情发生,具体的业务逻辑操作和state的更新是交给Reducer来处理,它接收一个之前的 state和一个 Action;并基于此 Action 将会产生的影响,返回一个新的 state。

//count.js

import * as types from '../actions/types';

const initialState = {
count: 0
};

export default function counter(state = initialState, action = {}) {
switch (action.type) {
  case types.INCREMENT:
    return {
      ...state,
      count: state.count + 1
    };
  case types.DECREMENT:
    return {
      ...state,
      count: state.count - 1
    };
  default:
    return state;
}
}

需要创建一个index.js用来导出reducers,否则也会报错找不到index.js:

import count from './count';

export {
  count
};

Reducer可以不止一个,我们在设计的时候可以根据实际的业务逻辑来构建若干个Reducer。但是最终传递给store的需要是一个Reducer。这里Redux提供了combineReducers方法,把reducers组合成一个传递给store。

//合并多个reducer
const rootReducer = combineReducers({ reducer1, reducer2,  reducer3}); 

. Store

Store 就是把 Reducer 和 action 联系到一起的桥梁。Store 接收 Action 的数据并将其连同当前的 state树 (包含所有 state 的一种特殊的数据结构,是一个单一的对象)发给 Reducer

Store 有以下职责:

  1. 维持应用的 state;
  2. 提供 getState() 方法获取 state;
  3. 提供 dispatch(action) 方法更新 state;
  4. 接收新的state,并替换当前的state;
  5. state变化时,store触发事件;
  6. 通过 subscribe(listener) 注册监听器的组件从store提取新的state并更新组件。

Store本质上是一个对象,它以树的形式保存了整个应用的State。并提供了一些方法。例如getState( ) 和 dispatch( )。Redux应用只有惟一一个Store。Store通过createStore方法来创建,根据整个应用的根Reducer的初始State。

//配置store

import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import * as reducers from './reducers'

//添加中间件
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer);

React-Redux

Redux可以被任何的javascript框架应用。但是它和React或者React Native配合得非常完美。原因就是React和React Native的组件都是基于state来渲染视图的。而Redux正是围绕着state的管理而构建起来的应用框架。
React-Redux是React官方提供的库。通过这个库,我们可以很顺畅的使用Redux架构来构建React或React Native应用。github地址
React-Redux提供了两个API:

render() {
    return (
      <Provider store = {store}>
        <App />
      </Provider>
    );
}
//完整的root.js

'use strict';

import React, {Component} from 'react';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import * as reducers from './reducers'

import App from './app'

const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer);

export default class Root extends Component {
  render() {
    return (
      <Provider store = {store}>
        <App />
      </Provider>
    );
  }
}
//App.js

'use strict';

import React, { Component } from 'react';
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as actions from './actions/actions';


import FirstPage from './pages/firstPage';
import TwoPage from './pages/twoPage';

class App extends Component {

  constructor(props) {
    super(props);
  }

  render() {

    const { state, actions } = this.props;

    return (
      <FirstPage 
      count={state.count} 
      {...actions}/>
    ); 
  }
}

//connect负责把React Component 和 Redux store 结合起来。通过connect,你就可以拿到store中的state,并转化成Component的props来使用了
export default connect(state => ({
  //counter是reducer的文件名,否则会报返回不是一个对象的错误
    state: state.count
  }),
  (dispatch) => ({
 //counterActions要包含组件触发的action,需要在改组件里导入相应的action
    actions: bindActionCreators(actions, dispatch)
  })
)(App);

关于这两个API的详细说明,请参考官方文档

<Provider store>使应用底层的component的connect( )方法能够获取到store。通常<Provider store>我们都用来包在整个应用根Component的最外层,这样保证所有Component都能拿到store

Middleware

在redux里,middleware是发送action和action到达reducer之间的第三方扩展,也就是中间层。也可以这样说,middleware是架在action和store之间的一座桥梁。如果不使用middleware的话,Redux的store只支持同步数据流。也就是每当我们dispatch action时,state会被立即更新。同步只返回一个普通action对象。而异步操作中途会返回一个promise函数。当然在promise函数处理完毕后也会返回一个普通action对象。thunk中间件就是判断如果返回的是函数,则不传导给reducer,直到检测到是普通action对象,才交由reducer处理。

使用支持异步的middleware比如 redux-thunk或 redux-promise 能让我们实现异步的数据流。你可以使用applyMiddleware( ) 来增强 createStore( ) 。类似这样:

const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer);

我们也可以编写自己的中间件,来方便我们Debug,比如打印调用的action名称。

引入Redux框架前后组件代码的对比:

引用前:

'use strict';

import React, { Component } from 'react';

import {
  StyleSheet,
  View,
  Text,
  TouchableOpacity
} from 'react-native';

class TwoPage extends Component {

  constructor(props) {
    super(props);
    this.state= {
      count:0
    };
  }

  increment() {
    this.setState({
      count:this.state.count+1
    });
  }  

  decrement() {
    this.setState({
      count:this.state.count-1
    });
  }

  render() {
    
    return (
      <View style={styles.container}>
        <Text style={styles.text}>{this.state.count}</Text>

        <TouchableOpacity onPress={()=> this.increment()} style={styles.button}>
            <Text>Up</Text>
        </TouchableOpacity>

        <TouchableOpacity onPress={()=>this.decrement()} style={styles.button}>
            <Text>Down</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
    container:{
        flex:1,
        backgroundColor:'#4ec300',
        justifyContent:'center',
        alignItems:'center'
    },
    text:{
        fontSize:50,
        color:'#fff'
    },
    button: {
    width: 100,
    height: 30,
    padding: 10,
    backgroundColor: 'lightgray',
    alignItems: 'center',
    justifyContent: 'center',
    margin: 3
  }
});
export default TwoPage;

引用后:

'use strict';

import React, { Component } from 'react';

import {
  StyleSheet,
  View,
  Text,
  TouchableOpacity
} from 'react-native';

class FirstPage extends Component {

  render() {
    
    const { count, increment, decrement } = this.props;

    return (
      <View style={styles.container}>
        <Text style={styles.text}>{count}</Text>

        <TouchableOpacity onPress={increment} style={styles.button}>
            <Text>Up</Text>
        </TouchableOpacity>

        <TouchableOpacity onPress={decrement} style={styles.button}>
            <Text>Down</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
    container:{
        flex:1,
        backgroundColor:'#4ec300',
        justifyContent:'center',
        alignItems:'center'
    },
    text:{
        fontSize:50,
        color:'#fff'
    },
    button: {
    width: 100,
    height: 30,
    padding: 10,
    backgroundColor: 'lightgray',
    alignItems: 'center',
    justifyContent: 'center',
    margin: 3
  }
});

export default FirstPage;

通过对比引用Redux前后对比,大家再慢慢体会一下Redux框架吧!

由于我自己也是刚学习Redux框架,所以还没有在实际项目中引用,也是参考一些Demo和网上的教程边参考学习边体会代码。在RN中使用Redux看起来很麻烦也很难理解,只要跟着demo去边敲代码边理解就能够很容易掌握它。我之前也只是一直看博客什么的,但是不用代码去简单的实现它,理解起来确实很困难,更别提去实际使用它了。根据Demo去做可以加深我们的理解,毕竟“纸上得来终觉浅,绝知此事要躬行”嘛!

这里贴出GitHub上实现Redux框架的Demo:

https://github.com/ninty90/react-native-redux-demo
https://github.com/alinz/example-react-native-redux

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

推荐阅读更多精彩内容