学过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/