在react-native中使用redux

作者: 巨巨 | 来源:发表于2016-04-29 10:53 被阅读45707次

    [2017-02-17 Update,github上代码的RN版本已更新至最新版本(0.41.2),如有问题请提issue,3Q]

    redux是什么?

    redux是一个用于管理js应用状态的容器。redux出现时间并不是很长,在它出现之前也有类似功能的模块出现,诸如flux等等。redux设计的理念很简单,似乎最初这个开发团队就有让redux可以方便融入在server, browser, mobile client端的打算。目前在github上redux-*的第三方中间件、插件越来越多。如果react项目中想使用redux,那么就有react-redux插件来完成配合。而作为开发者,可以使用这些优秀的第三方资源来开发/优化已有的项目,也是一件很欢乐的事。

    设计的动机

    在redux.js.org里有关于编写redux的动机描述(our code must manage more state than ever before),在现实的生活中,单页应用越来越多,而且需要维护的状态也越来越复杂,诸如维护数据更新、UI更新、本地数据存储等这些都是我们在js应用常常需要处理的情景,当然这里多数都会涉及到异步处理。而redux本身就是为了解决这些问题,而是将所有的变化进行统一流程处理,会使我们的程序状态变化清晰可见。redux最终目的就是让状态(state)变化变得可预测。

    使用的三原则

    • 1, Single source of truth
      单一数据源。整个应用的state,存储在唯一一个object中,同时也只有一个store用于存储这个object.
    • 2, State is read-only
      状态是只读的。唯一能改变state的方法,就是触发action操作。action是用来描述正在发生的事件的一个对象。
    • ** 3, Changes are made with pure functions**
      在改变state tree时,用到action,同时也需要编写对应的reducers才能完成state改变操作。

    在上面的三原则中,我们看到了store, action, reducer这些词,那就先说说redux是怎么进行应用状态(state)维护管理的呢。

    redux状态管理的流程

    • action是用户触发或程序触发的一个普通对象。
    • reducer是根据action操作来做出不同的数据响应,返回一个新的state。
    • store的最终值就是由reducer的值来确定的。(一个store是一个对象, reducer会改变store中的某些值)


      redux流程.png

    上图简单画了下redux状态改变的流程。action -> reducer -> 新store -> 反馈到UI上有所改变
    下面再给个具体实例:

    redux登录实例.png

    store用于维护状态的容器,包括了应用的多个状态,比如说用户是否登录、用户信息、用户任务等等。action是一个普通对象,用于指明是哪种操作,这样才能在reducers中进行识别。而众多reducer是负责返回新的state的函数。在实际应用中,你需要将store或store的某个值绑定到界面,这样更新store的时候,该页面可以监听到值的更新,然后进行一些页面更新操作/跳转操作等。

    redux在实际使用中需要用到的高级部分

    redux设计如此简洁,以至于并没有进行异步处理的功能。但是留下了middleware这个概念。可以自己编写符合需要的中间件。目前第三方的中间件基本可以完成一个复杂应用的架构设计。那就先说一说,怎么去处理异步请求呢。
    首先推荐redux-thunk,可以看到它的源码很简洁。就是判断action是否是函数,如果是函数进行递归式的操作。所以在redux中的异步,只能出现在action中,而且还需要有中间件的支持。

     export default function thunkMiddleware({ dispatch, getState }) {
           return next => action => {
              if (typeof action === 'function') {
                  return action(dispatch, getState);
              }
            return next(action);
          };
     }
    // redux-thunk的源码
    

    同步action与异步action最大的区别是:
    同步只返回一个普通action对象。而异步操作中途会返回一个promise函数。当然在promise函数处理完毕后也会返回一个普通action对象。thunk中间件就是判断如果返回的是函数,则不传导给reducer,直到检测到是普通action对象,才交由reducer处理。

    实例说说redux的使用。

    实例代码在github上查看地址。该实例只演示了登录过程,是比较基础的redux使用案例。

    目录结构

    项目相关代码均在js目录下。目录中可以看到actions, reducers, store等子目录。

    实例action

    actions/user.js

    actions/user.js目录中定义了用户登录操作的action creator

    • 第22行的logIn
      logIn是一个异步action,注意函数内部的写法与redux-thunk的定义要相同。
    • 第38行的skipLogin
    • 第47行logOut
      这些creator,产生的action状态有LOGGED_DOING, LOGGED_IN, LOGGED_ERROR, LOGGED_OUT四种状态。
      第8行变量testUser, 第15行变量skipUser分别是模拟针对普通登录成功后(这里用的是fetch www.baidu.com,真实情况下需换成真实的登录接口)的用户对象,跳过登录后默认的用户对象。
      接下来再看看reducers怎么去处理这些action?

    实例reducer

    reducers/user.js

    reducers/user.js中:

    • 第5行的initialState定义了最开始的应用状态(即用户未登录的情况下的state)。
    • 第13行,对每个传过来的action进行switch,每个action都需要返回一个state对象,如果不需要变动,则返回原对象(switch中的default返回值)。需要变动,则返回一个新的state, 可以看到当type为LOGGED_DOING,LOGGED_IN, LOGGED_OUT时,返回的对象跟原始对象都会有一些字段的差别。
    reducers/index.js

    reducers/index.js中:

    • 第5行combineReducers是将应用的state进行组合。
    • 目前demo中只有用户信息,所以只看到第6行userStore这一个key,在一个业务复杂的应用里,需要保存很多应用和用户交互产生的信息(比如说用户聊天列表等信息)。

    实例store

    再来看看store的处理:

    store/index.js

    store/index.js中定义了store的行为(包括中间件):

    • 第23行的applyMiddleware会将中间件应用在redux action过程中。
    • 第10行自定义一个logger中间件,该中间件的目的是打印出当前的触发的action以及出发后的state变化。
    • 第27行,33行 使用了redux-persist这个第三方插件来将store对象存储到本地,以及从本地恢复数据到store中,比如说保存登录信息,下次打开应用可以直接跳过登录界面。

    程序入口

    上面是定义。下面再来看如何在程序中使用:

    程序入口index.js
    在入口文件index.js中:
    • 第27行,需要将store作为属性传递给Provider组件中。
    • 第28行,可以看到的是真正渲染出的东西是<Root/>标签返回的东西。

    那就来看看root.js里的内容。

    root.js
    root.js中:
    • 第48行的render主要实现了Navigator导航器的处理,并用自定义的一个Router(第27行)进行封装了下。
    • 第59行,在末尾的select函数,是将store中的某些值复制到当前组件的props中,注意这里需要用connect函数进行绑定,否则store变化,不会反馈到Root组件中。isLoggedIn这个变量便被复制到当前的Root组件中,在Root内部方法中可以访问。
    • 第16行,constructor里,会对登录状态进行判断,如果检测到已经登录了,则会修改Navigator初始的路由设置(第10行设置的),使应用直接显示MainPage

    登录页面。

    那再看看登录页面pages/login.js中的用户操作:

    pages/login.js - 1 pages/login.js - 2 pages/login.js - 3

    上面的3个图片是整个pages/login.js的源码。

    • 第160行,指定了该组件会与store的哪些值进行连接。分别是isLoggedIn, user, status
    • 第129行,定义了TextonPress操作。handleLogin是绑定的操作方法,方法内容见第60行。在71行时,会触发logIn这个action。
    • 触发logIn后,会先进入LOGGED_DOING状态,此时说明在登录中。在第42行,有对该状态进行监听,如果为该状态,会将弹窗弹出,提醒为loading态。
    • 第33行,在shouldComponentUpdate中,对即将变化的nextProps进行与目前的props进行对比。比如说logIn执行登录完成后,第35行检测到isLoggedIn为true,则执行toMain函数(即跳转到主页中)。

    看下最终的demo交互效果。

    demo交互效果

    推荐:
    RNTools是一个分享React Native文章、实例代码以及第三方模块的平台。RNTools官网链接 RNTools应用下载

    相关文章

      网友评论

      • 宁夏灼雪__:啊...意思大概是懂了,但是自己的ES6太差了,很多语法都看不懂
      • 大城市流浪者:你这样写可以不用thunk中间件了吧
      • 69355e794e7f:跑起来的页面显示的页面跟建立新的项目页面一摸一样啊,这是为什么啊?可以加你个联系方式吗?
        巨巨:@叙述你的爱 安卓并没有支持。
      • 69355e794e7f:为什么 我运行不起来那,我走react-native run-android 一直走................................这个,请问这是什么原因啊?
      • 5e64f6d1fc21:巨巨,你是妹子吗
      • 82c7d8841fc7:博主用sublime么,使用什么插件
      • 冷洪林:成都React Native交流群:647393547
      • 简单背包客:写的可以,很详细
      • 恍然如梦_b700:很好,我初学者 ,看到这个让我知道的了一些用途。
      • b2eeea3caa1e:a=>b=>c=>{...}这句是什么意思呢 是function(a){function(b){function(c){...}}}吗
        巨巨:@小白_5d63 a是参数,调用时候传递的。
        b2eeea3caa1e:@巨巨 还是不太懂 如果是这样的话 那个a有什么用呢
        巨巨:a => b,说明参数是a,返回值是b,后面的类推。
      • 炜_f0e4:写的蛮好
      • Ryannnn:大神太厉害了
      • a57fb4760d5c:git地址是多少
      • 5fd0aa446622:大神你好,为什么shouldComponentUpdate这里比较的是nextProps而不是nextState,redux不是管理的state嘛?谢谢
        5fd0aa446622:@巨巨 谢谢楼主回复,楼主可以讲讲那个打印日志的中间件嘛,没太懂
        巨巨:connect将redux中返回的数据绑定到props里面了,所以在shouldComponentUpate函数里是对比nextProps,该函数还有另外两个函数,nextState, nextContext,在实际情况中,第一个参数用的最多。
      • keyuan0214:写的很棒!看完之后完全明白了redux的框架结构,文件。尤其作者结合登录这样的示例,讲的特别细致,很详细,很棒!赞!
      • 1ef0cf8e43fd:大神, 你的function configStore(onComplete:()=>void) 这个在VSCODE上报错,不知道是什么问题
        巨巨:这个是flow的一种写法。你可以改成function configStore(onComplete)这样,上述flow的意思就是onComplete是一个函数,且该函数无参数,返回值为空.
      • jxz8777:大神啊 为啥Android跑不了呢..
        jxz8777:@巨巨 是的
        巨巨:没做适配。你是没有mac环境吗?
      • 薛定谔没有猫:很详细,谢谢。正需要
      • 43ec5675ebd2:react-native-redux-demo-master/js/pages/login.js中connect为什么没有绑定dispatch?如
        export default connect(select,dispatch=>({
        actions: bindActionCreators(userActions, dispatch)
        }))(LoginPage);
        巨巨:@明天更好 这个可以绑定,也可以不绑定,不绑定的话,就是无法调用props.actions.xxxx,但是不一定非得这么写。
      • 移动端_小刚哥:你好,你这个文章写得很好,我学习了很多东西,但是看着很费劲,这是我基础弱的原因,不过你这个项目也有问题,跑起来之后我发现有死循环的问题.
        巨巨:@iOS_小刚哥 用最新的版本试一试。应该不会有该问题了
        巨巨:@纯洁的小袋子 我更新到最新的版本了。可以重新构建试一试
        纯洁的小袋子:的确,哪里死循环了。
      • smartphp:连接器和适配器connect 楼主你没讲啊。
      • 牧馬放飏:大哥 你代码太屌 我看不懂 能不能加个QQ 534098845 (ˇˍˇ) 想~向您请教
      • 11dd回眸11:请问actions与reducers是怎么进行关联的
        巨巨:@11dd回眸11 dispatch方法发送action,会触发reducers去检测。
      • ad6850e88ae6:基于redux的项目怎么初始化的?
        巨巨:@无心禅师 推荐看看redux官网,react native init只是创建一个React native项目,跟redux没有关系
      • ad6850e88ae6:react native init?
      • ad6850e88ae6:这个项目怎么初始化的
      • AmyConan:为啥打包报错,MainActivity.java:25方法不会覆盖活实现超类型的方法
        AmyConan:@AmyConan 命令行 react-native run-android
      • 8bef82b761ae:现在项目跑不起来吗?
        8bef82b761ae:@马铃豆 我现在是按照这个es6做的
        马铃豆:@梦儿飘
        代码中有些模块需要从 "react" 中引入,可以参照下面来修改
        import React, { Component, PropTypes } from 'react';
        import {View, ScrollView, StyleSheet, Text, Navigator} from 'react-native';
        我使用的版本是 0.36, 目前最新的。
        巨巨:@梦儿飘 那可能是版本比较老了
      • 51efd20b1dc8:It's so kind of you. :smile:
      • 聆听者JYZ:你好,我也是第一次接触redux,我看项目数据以userStore贯穿,但是我不明白的是,这个userStore是怎么来的,我看项目中都是在用,没有看到他是在哪里定义的。
        聆听者JYZ:@巨巨 嗯嗯,好的,找到了,很感谢你,这篇文章对于我帮助很大
        巨巨:@聆听者JYZ 看下reducer目录文件。在index里面定义的
      • 一方天地sq:为什么我看到一些别的例子并没有用shucomentupdate方法进行更新,能否有别的方法
        巨巨:@759a2effe413 都是可以的,我那个是调用的手动更新,建议看官方文档
      • lanegg:赞~~ 写的很详细,初次接触redux,看了之后思路变清晰了
      • walkertong:这篇文章写的非常好,赞一个
      • 要开心:大神,你写的很详细,但是文章真的真的太长了,我结合demo都看完了,就感觉晕乎乎的没个贯穿的线,不知道头尾在哪里,我该怎么办啊,你能不能帮我拨下云?
        巨巨:@要开心 :+1:
        要开心:沧海桑田啊,你回复的时候,我已经写了几个月的RN了,redux也能用了。。。。
        smartphp: @要开心 呵呵,这个redux是没有办法速成的。需要慢慢去体会。我是一个初级初级的学习者。

      本文标题:在react-native中使用redux

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