ReactNative之Redux详解

用redux有一段时间了,感受仍是有必要把其相关的知识点系统的总结一下的,毕竟好记性不如烂笔头。上篇博客更新了关于《ES6中的迭代器、Generator函数以及Generator函数的异步操做》的内容,该内容时saga的基础,稍后会总结saga相关知识点。按部就班,本篇博客主要总结的是Redux相关的内容,而后下篇博客打算总结一下react-redux, 以及redux-thunk、redux-saga中间件。html

 

1、Redux与iOS中的Notification的比较

Redux 的功能和做用就是让State管理更为集中,由于在redux中全部的状态都是存储在Store中的,而在页面的各个模块中均可以去访问和修改Store中存储的状态值。从这一点来看,redux能够很好的解决一个页面中多个模块间的状态共享的问题。react

Redux这框架理解起来是比较简单的,这个框架自己也是比较小的,涉及的API也很是少。虽然小,但小而精。使用起来仍是满顺手的,大道至简。下方是Redux中的几个关键的词及对应的功能,理解完下方的几个关键词,Redux这个框架也就大概了解了。redux

  • Store : 从字面意思看,Store是存储、储存的意思,在 Redux 中,把相关的状态存储在了Store中,在Redux中Store能够看作是一个单例对象。而且Store中提供了一些API来操做这些状态,以下所示:
    • getState : 该方法用来获取Store中当前存储的状态值。
    • subscribe(listener回调方法): 用来监听Store中状态值的改变,状态值改变后会执行相关回调方法。
    • dispatch (action) : 该方法用来修改Store中存储的状态值,而Action就是一个普通的对象,其中能够携带一些修改特定状态时的一些信息。
  • Action: 上面也说了,而Action就是一个普通的对象,其中能够携带一些修改特定状态时的一些信息,被用来做为dispatch()方法的参数的。起到了媒介的做用,Action自己会携带一些信息,便于状态的修改。
  • Reducer: Reducer本质上是一个 方法集合的称呼,而这些方法的入参是 当前的State和Action,出参是被修改后的新的State对象,也就是说 dispatch 一个Action会执行一个Reducer。而Reducer对应方法,会根据Action携带的信息来修改State对象,并把修改后的State对象返回出去。固然返回这新的State会更新到Store中,从而会触发一系列的监听操做。

Redux的工做模式虽然是管理状态的,可是使用上我的感受更想通知。与iOS中的Notification工做方式即为类似,下方作了一些简单的类比。下方简单的画了一个类比的图,能够从下往上看,解释以下:后端

  • 通知中心:最下方是通知中心,对应着iOS的 NotificationCenter,主要用来注册、派发及移除通知的,因此的通知都会通过NotificationCenter的管理。在 Redux中,这个Store就扮演着 这个NotificationCenter的角色,用来管理全部的状态。不一样的时,Store中会存储各类状态。
  • 发送通知:若是要修改状态的值的话,得调用 Store中提供的 dispatch(事件派发) 方法来修改相关的状态,这个就好像 iOS中发送通知的Post方法。
  • 注册监听:而Store中的 subscribe 这个监听状态改变的方法,就相似于 Notification 中的 register方法,只有添加完监听的相关对象才能收到状态被修改的通知。
  • 通知对象:Store中的dispatch() 方法的参数 Action,就相似于 Notification 对象,用来携带通知或者状态修改的信息。
  • 执行方法:而 redux 中的 Reducer 就相似于执行通知的Selector,用来修改状态的。

 

2、经过加减法示例来看Redux的使用方式

下方经过一个简单的加减法程序来看一下Redux的使用方式。以前在介绍 iOS中的响应式框架 ReactiveCocoa 时写过相似的Demo,只不过今天咱们用 Redux 来实现一下。react-native

demo比较简单,就是两个加减法,输入的时候自动的修改计算的结果值。下方咱们就来简单的看一下RN中如何使用Redux来实现该功能。框架

 

一、建立Store  

首先建立Store,redux 专门提供了一个建立store的方法 createStore ,调用 createStore 时,咱们须要把修改State的Reducer方法传进去进行关联。下方的calculateReducer是自定义的一个修改State的方法,稍后会介绍。下方代码比较简单,就是建立了一个Store,并将该对象导了出去。  异步

 

 

 

 

二、建立Action

建立Store后,接下来咱们来建立对应的Action,下方代码就是对应的action文件中的内容。首先建立了一个 CountActionType 的对象,功能相似于枚举,其中 "ADD" 表明加法类型,"DESC" 表明减法类型。由于该示例中是在一个Reducer中处理的两个Action,因此得用 CountActionType 类型来判断派发的是哪一个Action,而后作对应的操做。ide

而后建立了一个 addTowNumbers 方法,该方法接收了一个参数,而后返回一个Action对象,其中Action对象的类型就是 ADD。 而下方的 descTowNumbers 方法返回的也是一个Action,该Action对应的是减法操做。稍后咱们会使用到该Action。函数

 

三、建立Reducer

下方的calculateReducer方法就是咱们建立的Reducer, 该方法接收两个参数,一个是State对象, 一个是Action对象。咱们给State对象赋了一个默认值, 这个默认值中有两个值,一个是表示加法结果的 addResult, 另外一个是表示减法结果的 descResult。post

Action对象中的payload对象中有两个值,及firstNumberhe和secondNumber,表示输入的两个值。而在Reducer中经过Action的Type字段来判断是作加法操做仍是减法操做。若是是Add则是加法操做,将payload中的两个值相加,而后将结果赋值给 state 中的addResult。若是是Desc的话,与Add相似,只不过作的是减法操做。

在该Reducer方法中,返回的是一个计算后端新的State。State被修改后,能够经过 Store 中的 subscribe 的方法进行监听该状态的改变。

 

 

四、AddTestView的实现

定义好Store、Action、Reducer, 接下来咱们就开始定义可操做的视图了。下方的AddTestView就是上面两个计算加减法的控件。下方是具体实现的说明:

在AddTestView中的构造方法中,咱们调用了 store 对象中的 subscribe 方法,传入了一个回调方法,来对Store中存储的状态进行监听,而后获取state中最新的状态,而后赋值给组件对应的State对象。

 

第二段核心的代码则是dispathAction了,在输入框变化后,会根据是Add仍是Desc调用下方的dispatchAction方法。若是是Add, 就会调用addTowNumber方法建立一个 加法动做对应的Action。若是是减法操做的话,则会调用 descTowNumber()方法建立一个减法对应的Action对象。而后把建立好的对象,经过store.dispatch(action) 方法派发出去。

store收到 Action后就会执行对应的 Reducer方法,而后去跟进Action提供的信息修改 Store中存储的State值。当State值被修改后,就会执行 subscriber 对应的回调方法获取最新的结果值,并赋值给组件内部的State对象进行展现。

 

 

下方AddTestView的所有代码。

// 仅仅使用redux
import React, { Component } from 'react';
import { Action } from 'redux';
import {Text, TouchableOpacity, View, StyleSheet, TextInput} from 'react-native';
import { store } from './store';
import {addTowNumbers, descTowNumbers, CountActionType} from './action';
const {
    DESC,
    ADD
} = CountActionType;

type State = {
    addResult: number,
    descResult: number
};

const styles = StyleSheet.create({
    textInput: {
        width: 60,
        borderRadius: 4,
        borderWidth: 0.5,
        borderColor: 'gray'
    },
    tipText: {
    }
});

export default class AddTestView extends Component<null, State> {
    addFirstNumber: string = '0';
    addSecondNumber: string = '0';
    descFirstNumber: string = '0';
    descSecondNumber: string = '0';

    constructor (props: any) {
        super(props);
        this.state = {
            addResult: 0,
            descResult: 0
        };
        store.subscribe(() => {
            const {
                addResult,
                descResult
            } = store.getState();
            this.setState({ addResult, descResult });
        });
    }

    firstTextChange = (type) => (text) => {
        if (type === CountActionType.ADD) {
            this.addFirstNumber = text;
            this.dispathAddAction();
        } else {
            this.descFirstNumber = text;
            this.dispathDescAction();
        }
    }

    secondTextChange = (type) => (text) => {
        if (type === CountActionType.ADD) {
            this.addSecondNumber = text;
            this.dispathAddAction();
        } else {
            this.descSecondNumber = text;
            this.dispathDescAction();
        }
    }

    dispathAddAction = () => {
        const action = addTowNumbers({firstNumber: this.addFirstNumber, secondNumber: this.addSecondNumber});
        store.dispatch(action);
    }

    dispathDescAction = () => {
        const action = descTowNumbers({firstNumber: this.descFirstNumber, secondNumber: this.descSecondNumber});
        store.dispatch(action);
    }

    calculate = (type) => {
        const calculateText = type === CountActionType.ADD ? '+' : '-';
        const result = type === CountActionType.ADD ? this.state.addResult : this.state.descResult;
        return (
            <View style={{flexDirection: 'row'}}>
                <TextInput style={styles.textInput} defaultValue={'0'} onChangeText = {this.firstTextChange(type)}/>
                <Text> {calculateText} </Text>
                <TextInput style={styles.textInput} defaultValue={'0'} onChangeText = {this.secondTextChange(type)}/>
                <Text> = </Text>
                <Text>{result}</Text>
            </View>
        );
    }

    render () {
        return (
            <View style={{ justifyContent: 'center', alignItems: 'center' }}>
                {this.calculate(CountActionType.ADD)}
                {this.calculate(CountActionType.DESC)}
            </View>
        );
    }
}
View Code

 

五、总结

介绍完相关的Demo,咱们能够总结一些具体的实现流程。上述各个部分的执行过程是比较简单的,下方是具体的总结:

  • Component 也就是下边的AddTestView 是不会直接调用 Reducer 方法来修改状态的,而是像 Store 经过Dispatch来派发Action的方式向Store下发修改State的命令。
  • Store在收到 Component 派发的 Action 后会调用对应的 Reducer。
  • Reducer则根据提供的Action信息来修改对应的State的值,并返回给Store,更新。
  • Component最终经过Subscribe的方式接收到更新后的State,固然派发 Action 的 Component 与 Subscriber 对应状态的 Component 大部分状况下不是一个。

 

 

上面是根据上述示例来画的简图,下方咱们能够脱离上述demo, 整理了一个图。从下图中不难看出,平时在开发时,Component通常是有多个的,而Store只有一个,这些Component都像Store派发Action修改对应的状态,而且能够经过Subscriber来监听对应状态值的改变。

而Reducer也能够是多个,建议将Reducer按照修改状态的类型或者相关的业务逻辑进行拆分,拆分红多个业务模块。修改不一样的状态时,会调用不一样的Reducer。

 

 

上述咱们是声明定义了一个Reducer ,若是修改State的东西都写在一个方法里,不免会有些难于维护。因此通常会对Reducer进行拆分,下方是对上述Reducer拆分后的代码。固然运行效果与以前的是同样的,下方也是推荐用法。

 

虽然该Demo, 使用Redux实现会比较麻烦,使用组件内部的State彻底能够实现,由于是为了窥探Redux的使用方式,因此咱们就用Redux实现了该demo。可是若是是跨组件的数据交流,该方式就比较合适了。

本篇博客就先到这儿吧,虽然本篇博客介绍了Redux, 可是在开发中不多直接使用,通常会结合着其余框架及中间件使用。以前还积累了一些 react-redux, 以及redux-thunkredux-saga 的东西,下篇博客把react-redux相关的东西在总结一下,作个记录也便于本身后期翻阅。最后附上redux的文档连接,有啥问题可翻阅https://www.redux.org.cn/

相关文章
相关标签/搜索