在React中使用Redux

作者: chardlau | 来源:发表于2017-06-28 17:22 被阅读18120次

    这是Webpack+React系列配置过程记录的第六篇。其他内容请参考:

    这篇文章的主要内容包括:

    1. 修改一下之前存在的问题;
    2. 在框架中引入redux,使用一个例子简单介绍redux的使用方法;
    3. 其他redux辅助库。

    修复遗留问题

    1. webpack.prod.config.js中缺少了对path库的引用,执行构建npm run build:prod的时候失败。在文件开始的地方引入node.js的path库就可以了。
    2. package.json里面定义了一个build:dev的脚本,这个脚本其实有点多余,不过有时候需要打包测试版本的文件,所以还是需要存在。主要有个问题是webpack.dev.config.js中output节点下错误定义了path的值为根目录'/',这在使用npm start命令启动运行时打包的时候看不出问题,但是在使用npm run build:dev时会出现无法写文件到根目录的权限错误。只要把path的值改掉就可以。path: config.publicPath改成path: config.staticPath,publicPath: config.publicPath
    3. css-loader和less-loader导出的样式类名太长,还是把localIdentName中的path部分去掉比较好看。

    redux

    安装redux

    安装依赖的命令如下:

    npm install --save redux react-redux redux-thunk
    npm install --save-dev redux-logger
    

    redux不用说了,我是把它当成一个本地数据库使用,react-redux帮助你完成数据订阅,redux-thunk可以放你实现异步action,redux-logger是redux的日志中间件。

    关于redux与代码布局

    在开始介绍之前我想先就redux的使用发表一些自己的看法:

    前文说了我把redux当成一个本地数据库,因此我倾向于把redux封装类似于mvc中的Model的角色,独立为一层。这与另一种观点——我在公司的项目更倾向于把每个页面当成一个独立模块,每个模块维护自己的reducer和action的观点,有所出入。

    我的做法可以更好地实现reducer的复用。而对我自己来说更重要的好处是集中修改。更适合小项目或者独自开发一个项目的场景。

    我公司的项目的做法对多人协同开发更有利,毕竟每个人维护好自己的代码就可以了。公司项目的这种方法有几个问题让我比较难以接受:

    第一个是模块越多reducer和action的定义越多,很多时候这些代码都是差不多的。

    更重要的是第二个问题:模块数据在store里面的存储是直接在根state下面排列下来的,根state的数据格式样式有点像这样:

    {
        aModuleData:{...},
        bModuleData:{...},
        cModuleData:{...},
        dModuleData:{...},
        ...
    }
    

    项目的原意是希望每个模块的保持独立,但实际上使用的时候却是有极大的可能出现aModule同时使用aModuleData和bModuleData的情况。这跟每个人维护自己的代码的初衷有悖,也没有发挥好redux的真正能力。

    还有一个小问题是reducer的组织通常影响着应用数据state的样式,把reducer分散到每个模块之后,state的形式在代码上很难直管地反映出来,特别是当模块是动态加载的时候更甚。不过借助logger等工具可以解决。

    关于这块的争议Redux的教程中有提及。

    使用redux

    无论代码怎么布局,使用redux的方法主要还是三步曲:创建store、创建action、创建reducer。而在这之后才是与业务或者组件相关的数据处理和展示。

    先看一下我的做法的代码布局:

    代码布局

    创建store的代码集中在model/index.js中,model/actions/.js和model/reducer/.js里面分别是写action创建函数和reducer函数的地方,根据模块可以自己DIY。

    model/index.js的代码如下:

    model/index.js的代码

    model/actions/index.js的代码如下:

    model/actions/index.js的代码

    这里定义了一个名叫login的异步actionCreator以及三个普通的actionCreator。

    actionCreator被某个组件调用后会向store发送action,然后被reducer处理,reducer定义在model/reducers/index.js中,代码如下:

    model/reducers/index.js的代码

    这就完成了三步曲了。上面的代码简单地模拟了登录的动作。登录页面用到的数据存放在loginPageData中,登陆后获取到的当前登录用户数据存储在实体数据entities中。

    接下来要把redux和react联系起来,也就是把redux的store中的数据交给react的组件使用。

    第一步需要挂载redux的store到react,为react提供数据支持。最简单的做法是找到应用的根组件(我这里是BasicExample.js),然后在它的render函数中最外层添加Providor标签。代码片段如下:

    挂载redux的store到react

    红线部分画出了改动点,从model/index.js中导出了store对象,通过react-redux提供的Providor标签挂载到react中,为react提供数据支持。

    看最后的红线中,我们在Home组件里面添加了这次的测试例子ReduxDemo。它的代码如下:

    ReduxDemo代码

    代码的重点在connect函数。这个函数也是由react-redux提供的。使用它可以包装普通的展示组件(这里是ReduxDemo——只负责展示数据),然后返回一个容器组件。connect函数通过第一个参数让展示组件订阅了来自store的数据;通过第二个参数让展示组件默认可以dispatch各种action。

    这个例子在ReduxDemo挂载完成后调用login接口模拟登陆。返回结果被塞到store中(数据格式由先前写好的reducers的组织方式决定)。页面根据store中的数据展示内容。由于login发出的远程请求是假的,所以这里总是失败,因此会显示失败的内容。

    关于redux的使用介绍到此结束。

    redux辅助库

    其实在上面的代码中我已经悄悄地提及了两个辅助库,也是我想在这里推荐的两个库:

    1. 开发工具redux-devtools:结合各种其他库可以实现可视化的调试界面。
    2. 数据规范化工具normalizr:规范化组织数据。经过三个项目的体验后,个人非常推荐使用这个库,可以让应用的数据组织更清晰、减少冗余数据、减少因数据刷新导致的性能影响。

    暂时不在这里展开介绍,有兴趣的可以到github上查一下文档。

    源码下载地址:https://pan.baidu.com/s/1dENYfuh

    相关文章

      网友评论

      • tikeyc:把源码放GitHub去吧,百度网盘没意思,而且我这限制入
      • cb9bdcc644d6:楼主……想请问一下后面的页面获取不到redux的store?
        用的是react-router4.0
        配置路由的页面store一个也取不到……
        chardlau:你说的页面是指组件吗?
        如果是组件的话没有直接获取store的方法。但如果你真有需要直接用到store对象,可以在创建store的js文件里export它,然后在组件里面import这个js就可以获取store对象了。需要注意的是不要重复创建store对象。
      • f55e293c8678:请问大神总是报错说Target container is not a DOM element.是什么情况,百度了说<script>写在html下面,就是这样写的啊
        chardlau:@都被占用了叫什么好 我也没遇到过。我猜是你是不是把React的Component当成Dom操作了吧。Component是伪节点并不是真的Dom节点。
        f55e293c8678:@chardlau 不好意思啊,最近很长一段时间没有在看简书了,react的搭建也暂时搁置了。我好像搭建没有太大问题,因为运行没有报错,但是页面没有任何效果,然后检查报的是这样的错:Uncaught Error: Target container is not a DOM element. 百度以后发现我的代码也没什么问题,请问您遇到过这种情况吗,怎么解决的
        chardlau:不好意思,我不是很明白你的问题,能详细描述一下你在什么情况下报错吗?
      • b6a97211cf9f:payload:loading 请问一下用payload有什么好处呢
        简心豆:这个是在数据加载的过程中,用来提示用户数据正在加载的,也就是用来控制我们经常看到的那个转圈圈的动画的
        chardlau:payload没有什么特殊意义,redux对Action的字段除了type有特殊要求,其他字段都是自定义的。payload只是我自己的一种用法,不必太在乎。我只是把Action看成是应用内通讯的一个报文,payload指代的是里面的数据
      • 简心豆:不是很明白那个login()是如何在componentDidMount里运行的,直接this.props.login()应该只是返回一个函数啊。
        简心豆:@chardlau 可是你这样的话,dispatch就是undefined,必须将它传入login,应该要用到bindActionCreator
        chardlau:这个login在调用后实际上返回的函数被redux-thunk接管了,redux-thunk能够让你发出异步Action,否则的化这里只能返回对象(如上面定义的loginSuccess函数那样)。
        至于详细的调用逻辑,我说不太明白,建议看一下Redux和Redux-thunk的源码
      • Andy_Sun:楼主,为何我按照你写的运行时候报dispatch is not defined
      • e51a6af457cb:大神,爱死你了!可以运行了
        chardlau:不好意思,最近一个月都在赶项目,所以没上简书,否则一定第一时间回应你的留言。:blush:
      • 花样棉花:加油!好棒
        花样棉花: @chardlau 不客气!撸起袖子加油干!随时欢迎你来我文章指出问题,共同进步!我非常需要你的帮助和支持!
        chardlau:谢谢你:grin:

      本文标题:在React中使用Redux

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