React Native集成Redux框架讲解与应用

      学过React Native的都知道,RN的UI是根据相应组件的state进行render的,而页面又是由大大小小的组件构成,导致每个组件都必须维护自身的一套状态,因此当页面复杂化的时候,管理state会相当吃力的。而redux提供了一套机制来组织管理整个应用状态。
   Redux有三部分组成:store,action,reducer。
   store:维护全局的state,以及将action和reducer结合起来。
   action:用来传递state的信息。(比如:我们在action中处理登陆操作,将返回的user对象传递给对应的reducer.)
   reducer:reducer是简单的处理函数,通过传入旧的state和指示操作的action来更新state,从而达到页面的刷新。

下面通过一个简单的例子来集成下。
首先安装相关库:
安装redux:npm install –save redux
安装redux绑定库:npm install –save React-redux
安装开发者工具:npm install –save-dev redux-devtools
安装异步action构造器:npm install –save redux-thunk
在集成之前熟悉下一般基于Redux的目录结构:
.
├── src #开发目录
| |
| ├──constants #ActionTypes和Urls
| |
| ├──actions #actions的文件
| |
| ├──components #内部组件
| |
| ├──containers #容器组件
| |
| ├──reducers #reducer文件
| |
| ├──stores #store配置文件
| |
| └──utils #工具
|
├── node_modules #包文件夹
├── .gitignore
├── index.js #入口文件
└── package.json
(如果你之前不了解Redux的话,或许会比较蒙圈,但不要紧,跟着流程多走几遍,试着推敲先分析下流程,慢慢就理解了)
/********************************store********************************/
1.首先创建全局的store。(一般在stores文件中写个配置文件)

[javascript] view plain copy 在CODE上查看代码片派生到我的代码片
'use strict';  

import { createStore, applyMiddleware ,combineReducers} from 'redux';  
import thunk from 'redux-thunk';//引入异步操作  
//引入所有的reducers,切记要在index.js封装下.  
import * as reducers from '../reducers';  
const middlewares = [thunk];  

const createSoreWithMiddleware=applyMiddleware(...middlewares)(createStore);  

//配置store信息  
export default function configureStore(initialState){  

  //将reducer组合起来  
  const reducer=combineReducers(reducers);  
  //创建store  
  const store=createSoreWithMiddleware(reducer,initialState);  

  return store;  
}  

简单讲解下:
首先引入该APP中所有的reducer,根据上面的目录结构,我们把所有的reducer放入到reducers文件中,切记要加入个index.js进行配置.上面很多都是固定格式,暂时先不分析为什么,做的目的就是返回一个全局的store.
store的应用(这里的APP就是我们应用的最顶层组件)。

[javascript] view plain copy 在CODE上查看代码片派生到我的代码片
import React, { Component } from 'react';  
import {Provider} from 'react-redux';  
import App from './containers/app';  

import configureStore from './store/configureStore';  
const store=configureStore();//获取store  

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

App组件其实可以把所有的页面加入到这里,全局进行控制(官方F8是这么操作的)。不过这里暂时先不这样处理,关于navigator的push,pop操作还是放到对应的页面进行处理,更符合我们的原生开发逻辑。
简单看下store中结构:

/********************************action********************************/
创建登陆对应的action:

[javascript] view plain copy 在CODE上查看代码片派生到我的代码片
 import * as types from './types';  
 import {Alert}from 'react-native';  

//登陆(登陆操作属于耗时操作,所以需要异步执行,这里采用dispatch分发)  
export function login(user){  
   return dispatch=>{  
     //登陆中,派遣给LOGIN_ING  
     dispatch({type:types.LOGIN_ING});  
     let result=fetch('http://www.baidu.com')  
                .then((res)=>{  
                  //延时2s为了模拟效果  
                  setTimeout(()=>{  
                    if(user.phone=='15221850400'&&user.password=='123456'){  
                      dispatch({type:types.LOGIN,user:user});  
                    }else{  
                      //这里分发的是action  
                      Alert.alert('用户名或密码错误');  

                      dispatch(error());  
                    }  
                  },1000);  
                }).catch((err)=>{  
                   alert(err);  
                   dispatch({type:types.LOGIN_ERROR});  
                })  
   }  
}  

function error(){  
  return {  
    type:types.LOGIN_ERROR  
  };  
}  

//登出(由于登出操作一般都只是清空一些数据,不需要异步执行直接返回就可以了,)  
export function logout(){  
  return {  
    type:types.LOGOUT,  
  };  
}  

逻辑还算简单,就只是做个用户名,密码判断,但或许会问dispatch是个什么玩意,哪来的呢,其实上面我们也截图出来了,这个方法是我们创建全局store中的方法。
至于action正常的应该只是一个含有type的json对象,但是为了扩展性,一般会写成函数的形式,俗称action creator如上面的logout方法.
至于login方法由于需要网络操作,固然是异步的,就好比我们原生开发的时候请求API的操作一般都会丢到一个线程中,通过Handler消息机制来渲染UI.
dispatch({type:types.LOGIN_ING}):根据相应的action来进行调用对应reducer方法。
/********************************reducer********************************/
接着我们看下最后一个reducer:

[javascript] view plain copy 在CODE上查看代码片派生到我的代码片
import * as types from '../actions/types';  

const initialState={  
  isLoggedIn:false,//登陆状态  
  user:{},  
  status: null,//登陆操作状态 ‘done’:已登陆,'doing':正在登陆,null:没有登陆  
};  

//reducer处理函数更新state,渲染UI(主要根据传入旧的state,)  
export default function user(state=initialState,action={}){  

  switch(action.type) {  
    case types.LOGIN:  
       return{  
         ...state,  
         isLoggedIn:true,  
         user:action.user,  
         status: 'done',  
       }  
      break;  
    case types.LOGIN_ING:  
      return {  
        ...state,  
        isLoggedIn:false,  
        status: 'doing',  
      }  
      break;  
    case types.LOGIN_ERROR:  
    console.log('types.LOGIN_ERROR...');  
        return{  
          ...state,  
            isLoggedIn: false,  
          status: null,  
        };  
        break;  
    case types.LOGOUT:  

      return {  
        ...state,  
        isLoggedIn:false,  
        status:null,  
      }  
      break;  
    //切莫忘记default返回值  
    default:  
      return state;  
  }  
}  

reducer其实就是根据一系列action的处理函数,好比我们在前面action中返回的有LOGIN,LOGIN_ING,LOGIN_ERROR等状态,然后调用reducer根据不同的type返回当前最新的state,然后再render ui。
/********************************connect********************************/
redux的三部分至此就操作完毕,但如果进行链接起来呢,这里就用到connect组件,connect是将某一个组件(这里一般指一个页面)和store链接起来,目的就是获取当前页面所需的state以及dispatch方法。(从全局state中获取这个页面需要的数据然后以props的形式传递给当前页面。)
[javascript] view plain copy 在CODE上查看代码片派生到我的代码片

import React, { Component } from 'react';  
import {  
  StyleSheet,  
  TextInput,  
  Text,  
  View,  
  TouchableHighlight,  
  ActivityIndicator,  
} from 'react-native';  

import {connect} from 'react-redux';//将我们的页面和action链接起来  
import {bindActionCreators} from 'redux';//将要绑定的actions和dispatch绑定到一起  
import * as actionCreators from '../actions/loginActions';//导入需要绑定的actions  
import Modal from 'react-native-modalbox';  
import Home from './home';  


/** 
登陆页面 
**/  
class Login extends Component{  

  constructor(props){  
    super(props);  

    this.state={  
    }  

    this.login=this.login.bind(this);  
    this.onChangePhone=this.onChangePhone.bind(this);  
    this.onChangePswd=this.onChangePswd.bind(this);  
  }  

   onChangePhone(text){  
     this.setState({'phone':text,});  
   }  

   onChangePswd(text){  
     this.setState({'password':text,});  
   }  

   login(){  

     if(!this.state.phone||!this.state.password){  
       alert('用户名或密码不能为空!');  
     }else{  
       this.refs.modal.open();//loading 状态  
       this.props.actions.login({'phone':this.state.phone,'password':this.state.password});//dispath 登陆  
     }  
   }  

   //该方法首次不会执行,如果返回false,则reduer不会执行,,  
   shouldComponentUpdate(nextProps,nextState){  
     const {isLoggedIn,navigator}=nextProps;  
      if(isLoggedIn){  
        this.setState({phone:'',password:''});  

        navigator.push({  
          component:Home,  
          name:'Home',  
        });  
      }  
     return true;  
   }  

   render(){  
    console.log('render...');  
     return(  
      <View style={{flex:1}}>  
      <View style={{padding:20,marginTop:50}}>  
      <View style={styles.item}><Text style={{width:70}}>手机号码</Text>  
      <TextInput  
      style={styles.input}  
      onChangeText={this.onChangePhone}  
      placeholder='请输入手机号码'  
      value={this.state.phone}  
      />  
      </View>  
      <View style={styles.item}>  
      <Text style={{width:70}}>密码</Text>  
      <TextInput  
      style={styles.input}  
      onChangeText={this.onChangePswd}  
      placeholder='请输入密码'  
      password={true}  
      value={this.state.password}  
      />  
      </View>  

      <TouchableHighlight style={styles.button}  
       underlayColor='#000000' onPress={this.login}>  
      <Text style={{fontSize:16,color:'#fff'}}>登陆</Text>  
      </TouchableHighlight>  
      </View>  

      <Modal  
      style={styles.modal}  
      ref='modal'  
      isOpen={this.props.status=='doing'?true:false}  
      animationDuration={0}  
      position={"center"}  
      >  
      <ActivityIndicator  
      size='large'  
      />  
      <Text style={{marginTop:15,fontSize:16,color:'#444444'}}>登陆中...</Text>  
      </Modal>  
      </View>  
     );  
   }  
}  

const styles =StyleSheet.create({  
    item:{  
      flex:1,  
      flexDirection:'row',  
      alignItems:'center',  
      height:50,  
      borderBottomColor:'#ddd',  
      borderBottomWidth:1,  
    },  
    input:{  
      flex:1,  
      fontSize:14,  
    },  
    button:{  
      backgroundColor:'#1a191f',  
      height:50,  
      marginTop:40,  
      justifyContent:'center',  
      alignItems:'center'  
    },  
    modal: {  
      justifyContent: 'center',  
      alignItems: 'center',  
      width:150,  
      height:150,  
      borderRadius:10,  
    },  
});  

//根据全局state返回当前页面所需要的信息,(注意以props的形式传递给Login)  
function mapStateToProps(state){  
  return{  
    isLoggedIn:state.user.isLoggedIn,  
    status:state.user.status,  
  };  
}  
//返回可以操作store.state的actions,(其实就是我们可以通过actions来调用我们绑定好的一系列方法)  
function mapDispatchToProps(dispatch){  
  return {  
      actions: bindActionCreators(actionCreators, dispatch)  
  };  
}  

//链接起来  
export default connect(mapStateToProps,mapDispatchToProps)(Login);  

上面的代码不用仔细看,主要是尾部的部分,
mapStateToProps方法:根据全局state返回当前页面所需的数据然后以props的形式传递给当前页面(Login)。
mapDispatchToProps:该方法就是将dispatch和当前页面引入的actionCreators绑定在一起,然后就可以轻松调用。
如:login方法中的:

this.props.actions.login({'phone':this.state.phone,'password':this.state.password});//dispath 登陆

这样整体集成就完毕了,这里简单总结下:
1.首先我们创建全局的store(基于所有的reducer)在APP最外层引用,然后我们创建action(可以根据页面或者某种类别来定义)。接着我们创建reducer(可以设计成跟action一一对应)。最后通过connect将它们和页面链接起来,至于action和reducer的内容,可以等页面编辑OK后再进行设计。
2.执行简单流程:在页面中首先调用对应action方法(传递参数)—>执行相应的业务逻辑,然后调用dispatch(action)(将结果以action的形式传递给reducer)—>在reducer中根据type字段然后返回最新的state,然后在进行render具体的ui。

转自:http://blog.csdn.net/jj120522/article/details/52071469
参考:http://redux.js.org/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值