react+redux教程(四)undo、devtools、router
上节课,我们介绍了一些es6的新语法:react+redux教程(三)reduce()、filter()、map()、some()、every()、...展开属性
今天我们通过解读redux-undo的官方示例代码来学习,在redux中使用撤销功能、devtools功能、以及router。
例子
这个例子是个计数器程序,包含计数器、右边的redux开发工具、还有一个路由(不过只有“/”这一个地址)。
源代码:
https://github.com/lewis617/react-redux-tutorial/tree/master/redux-undo-boilerplate
撤销
实现撤销功能非常简单,你只需要使你的reducers可以撤销就可以了。这是什么意思呢?看代码
reducers/index.js
- import { combineReducers } from 'redux'
- import counter from './counter'
- import {
- INCREMENT_COUNTER, DECREMENT_COUNTER,
- UNDO_COUNTER, REDO_COUNTER
- } from '../actions/counter'
- import undoable, { includeAction } from 'redux-undo'
- const rootReducer = combineReducers({
- counter: undoable(counter, {
- filter: includeAction([INCREMENT_COUNTER, DECREMENT_COUNTER]),
- limit: 10,
- debug: true,
- undoType: UNDO_COUNTER,
- redoType: REDO_COUNTER
- })
- })
- export default rootReducer
我们使用redux-undo这个包给我们提供的undoable和includeAction,就可以可以给指定reducer(counter)添加撤销功能。filter是选择过滤的action有哪些,这里我们只撤销重做加减action,也就是INCREMENT_COUNTER, DECREMENT_COUNTER,
limit是次数限制,debug是是否调试码,undotype和redotype是撤销重做的action。
如此以来,我只需要触发撤销重做的action便可以实现撤销重做功能,就是这么简单!
devtools
接下来,我们开始学习使用devtools这个功能,devtools是什么?devtools的实质其实也是组件。devtools能干什么?devtools可以帮助我们看到整个程序的状态和整个程序的触发的action的日志记录。我们如何安装devtools呢?首先,我们知道devtools是个组件,那么我们直接把devtools放在容器中渲染出来不就可以了吗?
containers/DevTools.js
- /*eslint-disable*/
- import React from 'react'
- import { createDevTools } from 'redux-devtools'
- import LogMonitor from 'redux-devtools-log-monitor'
- import DockMonitor from 'redux-devtools-dock-monitor'
- /*eslint-enable*/
- export default createDevTools(
- <DockMonitor toggleVisibilityKey="H"
- changePositionKey="Q">
- <LogMonitor />
- </DockMonitor>
- )
这是一个可以复用的容器代码,也就意味着,你可以直接把这个js文件,复制粘贴到你的项目中。这段代码我们输出了一个devtools组件。
containers/Root.js
- /* global __DEVTOOLS__ */
- /*eslint-disable*/
- import React, { Component, PropTypes } from 'react'
- import { Provider } from 'react-redux'
- import { Router } from 'react-router'
- import configureStore from '../store/configureStore'
- import routes from '../routes'
- /*eslint-enable*/
- const store = configureStore()
- function createElements (history) {
- const elements = [
- <Router key="router" history={history} children={routes} />
- ]
- if (typeof __DEVTOOLS__ !== 'undefined' && __DEVTOOLS__) {
- /*eslint-disable*/
- const DevTools = require('./DevTools')
- /*eslint-enable*/
- elements.push(<DevTools key="devtools" />)
- }
- return elements
- }
- export default class Root extends Component {
- static propTypes = {
- history: PropTypes.object.isRequired
- }
- render () {
- return (
- <Provider store={store} key="provider">
- <div>
- {createElements(this.props.history)}
- </div>
- </Provider>
- )
- }
- }
这段代码,我们将我们导出的devtools组件放在了router这个组件的下面,不过我们加了一个typeof __DEVTOOLS__ !== 'undefined' && __DEVTOOLS__的判断,如果条件成立,我们将渲染devtools,否则不渲染。这样做,意味着我们可以通过参数控制devtools在开发环境中显示,在生产环境中不显示。
是不是渲染出来,就可以了?当然不是!我们还需要在store里面注册!
store/configureStore.js
- /* global __DEVTOOLS__ */
- import { createStore, applyMiddleware, compose } from 'redux'
- // reducer
- import rootReducer from '../reducers'
- // middleware
- import thunkMiddleware from 'redux-thunk'
- import promiseMiddleware from 'redux-promise'
- import createLogger from 'redux-logger'
- const loggerMiddleware = createLogger({
- level: 'info',
- collapsed: true
- })
- const enforceImmutableMiddleware = require('redux-immutable-state-invariant')()
- let createStoreWithMiddleware
- if (typeof __DEVTOOLS__ !== 'undefined' && __DEVTOOLS__) {
- const { persistState } = require('redux-devtools')
- const DevTools = require('../containers/DevTools')
- createStoreWithMiddleware = compose(
- applyMiddleware(
- enforceImmutableMiddleware,
- thunkMiddleware,
- promiseMiddleware,
- loggerMiddleware
- ),
- DevTools.instrument(),
- persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
- )(createStore)
- } else {
- createStoreWithMiddleware = compose(
- applyMiddleware(thunkMiddleware, promiseMiddleware)
- )(createStore)
- }
- /**
- * Creates a preconfigured store.
- */
- export default function configureStore (initialState) {
- const store = createStoreWithMiddleware(rootReducer, initialState)
- if (module.hot) {
- // Enable Webpack hot module replacement for reducers
- module.hot.accept('../reducers', () => {
- const nextRootReducer = require('../reducers/index')
- store.replaceReducer(nextRootReducer)
- })
- }
- return store
- }
到此为止,devtools我们就安装好了,就是这么简单!把它渲染出来就可以了,可以放在整个程序的下面就可以了!
store enhancer
DevTools.instrument() 这行代码使得devtools可以使用了!有的同学会问,这个instrument()是什么鬼?官方称之为store enhancer,翻译过来就是store加强器,跟applymiddleware是一类,都是store加强器。那么store加强器,能干什么?store加强器可以重新构建一个更牛逼的store,来替换之前的基础版的store,让你的程序可以增加很多别的功能,比如appllymiddleware可以给你的redux增加中间件,使之可以拥有异步功能,日志功能等!
enforceImmutableMiddleware,thunkMiddleware, promiseMiddleware, loggerMiddleware
有的同学会问,enforceImmutableMiddleware是什么?干嘛用的?这个使用禁止你改变state的。什么?不改变state,我们如何更新状态,redux不允许你改变state,在reducer中我们必须要返回一个新的state,而不是修改原来的state!
那个thunk是什么?thunk我们已经在react+redux教程(一)connect、applyMiddleware、thunk、webpackHotMiddleware里面讲过了。
那么什么是promiseMiddleware?这也是个中间件,和thunk一样,使得你的action可以具备异步的功能。不过,我们可以发现,本例中我们并没有用到thunk和promiseMiddleware这两个中间件,本例子是个种子文件,可以在这个基础上拓展,所以作者提前写好了两个常用中间件,便于我们日后使用!
那么loggerMiddleware是用来干嘛的?顾名思义,就是用来记录日志的,当你添加这个中间件,你可以在命令行中看到相关的打印日志!当然你可以在运行程序的时候,去掉这个中间件,来对比观察它的作用!
instrument()与compose()写法
instrument()不同于applymiddleware,它只能用于开发环境,只能enable你的devtools组件!那么我们把applymiddleware和instrument用逗号隔开,为什么?这是compose写法,用来代替以前的函数嵌套!
router
简单来说,router也是个组件,一个多重视图的组件,这个组件可以通过切换url来切换视图,总之它还是个组件。既然是组件,我们只要把它渲染出来就可以了!
最顶层我们要渲染一个Router ,代码在containers/Root.js中,我们就不重复列出代码清单了。
然后我们开始渲染各个视图,这里我们只有一个视图,也就是目录是“/”的视图,我们把它渲染出来!
routes.js
- /*eslint-disable*/
- import React from 'react'
- import { Route } from 'react-router'
- import App from './containers/App'
- import * as containers from './containers'
- /*eslint-enable*/
- const {
- CounterPage
- } = containers
- export default (
- <Route component={App}>
- <Route path="/" component={CounterPage} />
- </Route>
- )
我们导出了一个视图,这个视图的组件是CounterPage。就是这么简单!
然后,我们在containers/Root.js,将它渲染到Router组件里面就可以了!
- <Router key="router" history={history} children={routes} />
我们可以发现,我们并没有把devtools这个组件放在路由组件里面。这意味着,无论你如何切换视图,devtools都一直会渲染出来!
当然react-router的api还有很多,我们只是用了很少一部分,我不建议专门阅读api文档,应该在项目中,遇到不会的查询api文档,这样对api的用法的理解会更加的深刻!
教程源代码及目录
如果您觉得本博客教程帮到了您,就赏颗星吧!
https://github.com/lewis617/react-redux-tutorial
react+redux教程(四)undo、devtools、router的更多相关文章
- react+redux教程(五)异步、单一state树结构、componentWillReceiveProps
今天,我们要讲解的是异步.单一state树结构.componentWillReceiveProps这三个知识点. 例子 这个例子是官方的例子,主要是从Reddit中请求新闻列表来显示,可以切换reac ...
- react+redux教程(二)redux的单一状态树完全替代了react的状态机?
上篇react+redux教程,我们讲解了官方计数器的代码实现,react+redux教程(一).我们发现我们没有用到react组件本身的state,而是通过props来导入数据和操作的. 我们知道r ...
- react+redux教程(六)redux服务端渲染流程
今天,我们要讲解的是react+redux服务端渲染.个人认为,react击败angular的真正“杀手锏”就是服务端渲染.我们为什么要实现服务端渲染,主要是为了SEO. 例子 例子仍然是官方的计数器 ...
- react+redux教程(三)reduce()、filter()、map()、some()、every()、...展开属性
reduce().filter().map().some().every()....展开属性 这些概念属于es5.es6中的语法,跟react+redux并没有什么联系,我们直接在https:// ...
- react+redux教程(一)connect、applyMiddleware、thunk、webpackHotMiddleware
今天,我们通过解读官方示例代码(counter)的方式来学习react+redux. 例子 这个例子是官方的例子,计数器程序.前两个按钮是加减,第三个是如果当前数字是奇数则加一,第四个按钮是异步加一( ...
- react+redux教程(八)连接数据库的redux程序
前面所有的教程都是解读官方的示例代码,是时候我们自己写个连接数据库的redux程序了! 例子 这个例子代码,是我自己写的程序,一个非常简单的todo,但是包含了redux插件的用法,中间件的用法,连接 ...
- react+redux教程(七)自定义redux中间件
今天,我们要讲解的是自定义redux中间件这个知识点.本节内容非常抽象,特别是中间件的定义原理,那多层的函数嵌套和串联,需要极强逻辑思维能力才能完全消化吸收.不过我会多罗嗦几句,所以不用担心. 例子 ...
- react/redux组件库、模板、学习教程
开源的有蚂蚁金服的: 1.https://pro.ant.design/index-cn 2.https://pro.ant.design/docs/getting-started-cn 3.http ...
- React & Redux 的一些基本知识点
一.React.createClass 跟 React.Component 的区别在于后者使用了ES6的语法,用constructor构造器来构造默认的属性和状态. 1. React.createCl ...
随机推荐
- 常用的js正则表达式
正则表达式,一个十分古老而又强大的文本处理工具,仅仅用一段非常简短的表达式语句,便能够快速实现一个非常复杂的业务逻辑.熟练地掌握正则表达式的话,能够使你的开发效率得到极大的提升. 下面是一些,在前端开 ...
- MongooseHelper
/** * Created by lbc on 2016/11/16. */var mongoose=require("mongoose");var db=mongoose.con ...
- iOS 让按钮上的标题换行显示
项目中遇到了要让按钮上的文字换行显示的需求,就写了这个博客. 1.如果按钮上的文字固定,形式是写死的,可以考虑给标题文字加\n换行符来达到效果,但是,记得一定要设置这个属性,不然是不会换行的, but ...
- Python 比较两个字符串大小
python 2中,有cmp(a,b)函数,用于比较两个字符串的大小. 如 >>>a='abc' >>>b='abd' >>>print cmp( ...
- java身份证验证
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; impor ...
- androidannotations 简单复制与点击事件(1)
现在最火的android开发框架 简单描述一下 这一篇简单描述寻找控件以及事件的使用 1.该方法可以不用写setconteview @EActivity(R.layout.activity_main) ...
- ASP.net之策略模式
设计思路: 用ASP.net设计,调用策略模式.在第一个数和第二个数的文本框中输入数值,单击录题按钮,数值保存在n1,n2文档中,把要做的题都保存完后,单击开始按钮,开始做题,做完单击判断按钮,进行判 ...
- c++怎样让函数返回数组
这个问题属于非常初级的问题,但是对于初学不知道的人可能会比较头疼.C++中函数是不能直接返回一个数组的,但是数组其实就是指针,所以可以让函数返回指针来实现.比如一个矩阵相乘的函数,很容易地我们就写成: ...
- c++ eof()函数
C++ eof()函数可以帮助我们用来判断文件是否为空,抑或是判断其是否读到文件结尾.在这里我们将会对其进行详细的介绍. C++编程语言中的很多功能在我们的实际应用中起着非常大的作用.比如在对文件文本 ...
- Principles of measurement of sound intensity
Introduction In accordance with the definition of instantaneous sound intensity as the product of th ...