ReactNative引用Redux框架

作者: 心至靜行至遠 | 来源:发表于2016-11-02 17:12 被阅读1310次

    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

    相关文章

      网友评论

      本文标题:ReactNative引用Redux框架

      本文链接:https://www.haomeiwen.com/subject/kzeeuttx.html