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的目录结构:
(如果你之前不了解Redux的话,或许会比较蒙圈,但不要紧,跟着流程多走几遍,试着推敲先分析下流程,慢慢就理解了)
/*************************************store*************************************/
1.首先创建全局的store。(一般在stores文件中写个配置文件)
- '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就是我们应用的最顶层组件)。
- 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:
- 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:
- 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的形式传递给当前页面。)
- 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。
引用原文:https://blog.csdn.net/jj120522/article/details/52071469
可以参考:https://www.jianshu.com/p/4139babc6d5e
写博客是为了记住自己容易忘记的东西,另外也是对自己工作的总结,文章可以转载,无需版权。希望尽自己的努力,做到更好,大家一起努力进步!
如果有什么问题,欢迎大家一起探讨,代码如有问题,欢迎各位大神指正!