Redux之旅-2
Redux之旅-2
1. 前言上一篇文章,很简单的用代码来说明了State,Action,Reducer,Store各自的意义和相互的联系以及在实际开发的应用。但是在现实开发环境用,我们的需求不仅仅上一篇文章那么简单的。为了应付这些复杂的开发环境,我们还需要用到以下这些东西:
很明显,我们将要学习的这三个功能,对应回上一篇文章的Action,reducer,store。你可以认为这是它们各自的一些扩展。 2. 新的需求在上一篇文章我们的需求很简单。就是一个按钮,点击就让数字递增,那么今天为了学习新的东西,我们就增加一些新的需求。我们需求加两个按钮,一个按钮就是点击数字是+2,一个按钮是切换数字颜色。那么为了更方便我们的开发,我们的开发目录也应该修改一下。
3. Action Creator3.1 为什么需要Creator在Redux之旅-1里面,我们创建了一个很简单的例子:点击按钮递增。那么在更多的时候,我们的开发需求是很复杂的。那么增加一个点击就+2的按钮。这时候我们要怎么做? let IncreaseAction = {type:'increase'} 其中字符串increase的意思就是增加,那么我们现在的需求里面,一个点击+2的按钮,也符合increase这个意思。这时候的Action就不能像之前那样,直接写死。因为写死了就没办法让reducer知道,这个Action是+1还是+2了!而需要引入一个新的概念:Action Creator let IncreaseAction = (num = 1)=>{ return{ type:'increaseAction',num //看注1 } }
上面就是一个Action Creator,听起来很高大上的英文名词,其实就是一个帮我们返回Action的函数。那还记得Action的本质其实是什么吗?一个我们规定最了低限度要带着type属性的对象。那么其实就很简单了。Creator每次帮我们生成type都是increaseAction,但num不同的Action出来。这样,reducer就能分辨出我们的这个Action要+1还是+2 3.2 代码实现
//为了更加直观,我们把每个Action的type属性的值额外用一个文件去描述 export const INCREASE = 'increase' //增加数字的 export const THEME = 'theme' //切换主题的
import * as types from './types' export let increaseAction = (num = 1)=>{ return { type:types.INCREASE,num } } export let themeAction = ()=>{ return { type:types.THEME,} } OK!上面的代码中,我还顺便把切换主题的Action也写了,虽然它不需要传入参数去处理,但是为了以后的拓展性,用Action Creator总比用写死了的Action更佳,毕竟产品经理要改需求,谁也挡不住。 4. combineReducers4.1 分割和合并和Flux不同。Redux的store只有一个!只有一个store就意味着只有一个state。那么我们设计state的时候就很会纠结了。像现在这样子: let state = { count:0,//增加数字 theme:'#ffffff' //主题颜色 } 这并不是一个理想的state。一个理想的state应该要设计得很纯粹。不应该把无关得两个东西交错在一起。因为,我们需要分割开我们的reducer。每个不同的reducer下面仅仅处理自己拥有的state. let CountState = { count:0,//增加数字 } let ThemeState = { theme:'#ffffff' //主题颜色 } 这样的话,我们的state就可以设计得很小,并且很纯粹。而作为汇总,我们会用combineReducers把这些reducers合并回来。实际上state还是一大块,但是在reducer的角度来看,每个reducer都指示一小块。 4.2 代码实现
//处理数字增加的reducer import * as types from '../action/types' let reducer = (state={count:0},action)=>{ let count = state.count switch(action.type){ case types.INCREASE: //注意这里使用的action.num,明白是从哪里来的吗? return {count:count+action.num} break default: return state } } export default reducer
//处理主题的reducer import * as types from '../action/types' const writeColor = '#ffffff' const grayColor = '#cccccc' let reducer = (state={color:writeColor},action)=>{ let count = state.count switch(action.type){ case types.THEME: if(state.color == writeColor){ return {color:grayColor} } else { return {color:writeColor} } break default: return state } } export default reducer
//合并两个reducer import { combineReducers } from 'redux' import Increase from './increaseReducer' import Theme from './themeReducer' const rootReducer = combineReducers({ Increase,Theme }) export default rootReducer 5. Middleware5.1 什么叫做中间件在这里我并不打算深入去描述中间件这种概念,如果你是做node开发的话,又刚好用上了KOA的话,你会对这个东西很熟识。那么这里我简单的介绍一下什么是中间件。
在这里,有两个中间件是推荐使用的。 //用来打log的,开发时候用很方便 npm install --save-dev redux-logger //这个会在异步Action的时候用到,这篇文章没有到,不细说。 npm install --save redux-thunk
5.2 代码实现
'use strick' import {createStore,applyMiddleware} from 'redux' import createLogger from 'redux-logger' import rootReducer from '../reducer/rootReducer' /* 以前创建store的方式: let store = createStore(reducers) */ let createStoreWithMiddleware = applyMiddleware( createLogger(),)(createStore) let store = createStoreWithMiddleware(rootReducer) export default store 6. 连接组件和redux这里面和以前的差不多,但又有一些细节上的东西,我们需要注意的
'use strict' import React from 'react' //注意这里引入:bindActionCreators import { bindActionCreators } from 'redux' import { Provider,connect } from 'react-redux' //这里引入了组件 import Index from '../component/index' //引入了action creator 和 store。为啥没有引入reducer? //因为reducer在上面创建store的时候已经用到了 import * as actions from './action/creator' import store from './store/store' let {Component} = React /* mapStateToProps里面需要注意的是,由于我们的reducer是合并起来的,因此我们的state也是几个state拼起来。至于是哪几个state拼起来? 可以看回去rootReducer.js里面combineReducers的时候,里面的对象名字就是这里state的名字。 当然这里的return可以写成:return {state}也没所谓。但是为了大家能认清这个state里面有什么东西,这里写的稍微复杂一点 */ let mapStateToProps = (state) =>{ //return {state} return { reduxState:{ Increase:state.Increase,Theme:state.Theme,} } } /* mapDispatchToProps里面用到了bindActionCreators。关于bindActionCreators的作用看下面注释3 */ let mapDispatchToProps = (dispatch) =>{ return{ reduxActions:bindActionCreators(actions,dispatch) } } let Content = connect(mapStateToProps,mapDispatchToProps)(Index) class App extends Component{ render(){ return <Provider store={store}><Content /></Provider> } } export default App
7. 组件开发关于redux的开发在上面已经完成了,现在进入的是组件的开发,这里面更多的是满足例子而写的。没有太多的现实开发价值。但是我们可以在这里面很好的观察,我们写好的程序是怎样工作的。
'use strict' import React from 'react' let {Component} = React class Index extends Component{ constructor(props){ super(props) } //点击增加按钮事件 _addHandle(num){ /* 这里面可以想一下increaseAction的名字是怎么来的,同样下面主题切换按钮事件的themeAction又是怎么来的,代码之后为你揭秘。 */ let {increaseAction} = this.props.reduxActions increaseAction(num) } //主题切换按钮事件 _ThemeHandle(){ let { themeAction } = this.props.reduxActions themeAction() } render(){ //这里面输出props看看里面有什么东西 //console.log(this.props) let { reduxState } = this.props return ( <div style={styles.circle}> <div style={{color:reduxState.Theme.color}}>{reduxState.Increase.count}</div> <div style={styles.btnTheme} onClick={this._ThemeHandle.bind(this)}>切换主题</div> <div style={styles.btnAddOne} onClick={()=>{this._addHandle(1)}}>+1</div> <div style={styles.btnAddTwo} onClick={()=>{this._addHandle(2)}}>+2</div> </div> ) } } //样式定义,不用细看 const styles = { circle:{ width:'400px',height:'400px',position:'absolute',left:'50%',top:'50%',margin:'-200px 0 0 -200px',borderRadius:'50px',fontSize:'60px',color:'#545454',backgroundColor:'#ececec',lineHeight:'100px',textAlign:'center',},btnAddOne:{ width:'100px',height:'50px',lineHeight:'50px',backgroundColor:'#fcfcfc',fontSize:'20px',left:'40px',bottom:'10px',cursor:'pointer',btnAddTwo:{ width:'100px',right:'40px',btnTheme:{ width:'80%',right:'10%',bottom:'100px',} } export default Index 需要注意:在redux里面action/creator/reducer/store各种定义太多了。有时候你很难找到这个东西的名称是哪里定义过来的感觉。输出组件的props的话,我们得到这么一个东西 { reduxAction:{ increaseAction:fn(),themeAction:fn(),reduxState:{ Increase:count,Theme:color,} }
这里顺便贴出入口文件
'use strict' import 'babel-polyfill' import React from 'react'; import { render } from 'react-dom' import App from './redux/index'; render( <App />,document.getElementById('main') ); 如果没有做错,我们的程序能跑起来,如同下图一般
而控制台会在每次按钮之后,会如同下图一样
8. 总结和上一次说明state/Action/reducer/store的各种比如不同,这次需要说的分别是Action/reducer/store的一些扩展,为了更好的写程序,这些扩展是必不可少的,但会给新手一种把程序切割得很零散从而无从下手得感觉。所以这次我更加偏重于说明,每一个扩展存在得意义以及代码得实现,希望大家能看懂。 9.一些ES6的小知识在3.1的注1的时候,我们说了一个ES6的新特性:字面量增强 let c = 'abc' let obj = { a,//这个叫做键名简写 b(){ //这个是函数定义的简写 .... },[c]:12 // 这个是自定义键值写法,一个很有趣的语法糖 } 等于号后面我们叫做 字面量 let obj = { a:a,b:function(){ .... },abc:12 } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |