Redux之旅-1

作者: 三月懒驴 | 来源:发表于2016-03-31 17:23 被阅读1383次

    Redux之旅-1

    时间:2016.4.7-17:24
    作者:三月懒驴
    入门配置文章:链接

    准备

    在你的项目下面加入redux / react-redux

        npm install redux --save
        npm install react-redux --save
    

    入门小例子

    网上有很多关于Redux的解析了,我也不从抽象化去讲解整个redux的作用,而发现讲解Redux的编程化例子其实很少,所以在这里用代码来说明一下。什么叫做redux,以及它的Action/reducer/store

    看过我上一遍项目环境搭建的朋友应该有一个基本的项目目录架构。
    主要写代码的还是在app这个文件夹里面,里面的目录分布
    - component -->放置组件的
    - redux -->放置action && reducer
    - redux_lesson -->目前是放置用Provider打包出来的组件
    - main.js -->程序入口

    代码开始前的思考

    我们现在要做一个很简单的东西,用前端的话来说就是一个div标签,里面放置一个数字0,当我们点击这个div的时候,里面的数字就递增。这里面我们要进行一步就是,写代码前的思考。
    如上所说,我们的需求就是:点击,数字递增。那么我们的一些参数应该定义出来了。

    改变View的数据—state

    个人简单理解的state就是可以反映到view上的可变数据,这里我们的state定义如下

    state = {count:0}
    

    改变state的钥匙—Action

    同样是个人理解:state是可变的,但不是随便的可变,要改变它,就需要一把钥匙去打开这道大门,而action就是这把钥匙了
    我们把这个action定义成如下:

    increaseAction = {type:'increase'}
    

    Action 本质上是 JavaScript普通对象。我们约定,action内使用一个字符串类型的 type字段来表示将要执行的动作。多数情况下,type 会被定义成字符串常量。

    改变state的动作—Reducer

    个人的胡乱理解:有了state,有了要改变state的钥匙Action,那么谁来进行改变state的操作?reducer就是这么一个加工车间,你拿着原料(state)和钥匙(Action)进去总车间,用钥匙(Action)打开对应的生产线,生产出来新的产品(也是state)回去

    let reducer = (state={count:0},action)=>{
        //这里面传递进来两个参数,
        //一个是我们前面定义的state,如果木有传入的话,就用{count:0}
        //一个是我们前面定义的action,下面就要检查它的type来确定操作
        let count = state.count
        switch(action.type){
            //如果钥匙插对了孔,我们就返回进行了相应操作后的state对象
            case 'increase':
                return {count:count+1}
                break
            //如果钥匙都不对,就返回没操作过的state
            default:
                return state
        }
    }
    

    被我吃了的store

    因为相对前面三个东西来说,store是在太容易理解了,引入官方的话:

    Store 就是把它们联系到一起的对象。Redux 应用只有一个单一的 store。当需要拆分处理数据的逻辑时,使用 reducer 组合 而不是创建多个 store。

    开始真正写代码了

    其实上面的步骤我们都把一个redux处理数据的相关工作做的差不多了,那么接下来就是要真正的去写成程序

    创建action

    文件位置: app/redux/action.js

    export const increaseAction = {type:'increase'}
    

    创建reducer

    文件位置: app/redux/reudcer.js

    let reducer = (state={count:0},action)=>{
        let count = state.count
        switch(action.type){
            case 'increase':
                return {count:count+1}
                break
            default:
                return state
        }
    }
    export default reducer
    

    生成store,打包出新组件

    重要的事情:store只有一个!

    文件位置: app/redux_lesson/lesson_0.js

    'use strict'
    
    import React from 'react'
    import { createStore } from 'redux'
    import { Provider,connect } from 'react-redux'
    
    //这个index.js文件会在在下一步创建
    import Index from '../component/index'
    import reducers from '../redux/reducer'
    
    //创建store
    let store = createStore(reducers)
    /*
      mapStateToProps你可以理解成在下面connect的时候为组件提供一个props,这个props的值是redux的state
    */
    let mapStateToProps = (state) =>{
        return {count:state.count}
    }
    //连接你的state和最外层的组件
    let Content = connect(mapStateToProps)(Index)
    
    let {Component} = React
    
    //使用Provider来把新的App组件打包出来
    class App extends Component{
        render(){
            return <Provider store={store}><Content /></Provider>
        }
    }
    
    export default App
    

    创建View

    在View里面我们会接受到两个props。一个是在mapStateToProps生成的state,一个是store给我们的dispatch,这是是一个函数,我们用它的方法很简单粗暴,往里面传入一个Action就行了,它接受了这个Action就会告诉reducer去执行。

    文件位置: app/compoment/index.js

    'use strict'
    
    import React from 'react'
    import { connect } from 'react-redux'
    //请注意这里面引入了action
    import {increaseAction} from '../redux/action'
    let {Component,PropTypes}  = React
    class Index extends Component{
        //这一步是检查传入的各个prop类型是否正确
        ProTypes = {
            count:PropTypes.number.isRequired,
        }
        constructor(props){
            super(props)
        }
        handleClick(){
            /*
                这一步输入this.props可以看到,其实里面有两个东西
                在下面的render里面我们用到了this.props.count这个
                那么这里我们要用到dispatch
            */
            console.log(this.props)
            let {dispatch} = this.props
            //粗暴简单的使用
            dispatch(increaseAction)
        }
        render(){
            let {count} = this.props
            return <div onClick = {this.handleClick.bind(this)}  style={styles.circle}>{count}</div>
        }
    }
    //样式文件,不用细看
    let styles = {
        circle:{
            width:'100px',
            height:'100px',
            position:'absolute',
            left:'50%',
            top:'50%',
            margin:'-50px 0 0 -5px',
            borderRadius:'50px',
            fontSize:'60px',
            color:'#545454',
            backgroundColor:'#fcfcfc',
            lineHeight:'100px',
            textAlign:'center',
        }
    }
    export default Index
    
    

    进一步优化代码

    要做一个点击递增就需要那么多步骤是不是很烦恼?但是如果项目大起来之后,你想像这样,你就可以创建不同的钥匙Action,再编写不同的生产线reducer来修改各自的state,但是如上所做,我们的逻辑代码(点击递增)和View还是捆绑在一起(就是在组件里面使用dispatch)这个方法是不可取的。所以下一步我们就要进一步优化我们的代码

    文件位置: app/redux_lesson/lesson_0.js

    'use strict'
    
    import React from 'react'
    import { createStore } from 'redux'
    import { Provider,connect } from 'react-redux'
    
    import Index from '../component/index'
    import reducers from '../redux/reducer'
    /*
        注意:这里是新增的
        相对原来,我们在最外层打包这里引入Action
    */
    import {increaseAction} from '../redux/action'
    
    //创建store
    let store = createStore(reducers)
    /*
      mapStateToProps你可以理解成在下面connect的时候提供一个state
    */
    let mapStateToProps = (state) =>{
        return {count:state.count}
    }
    /*
        注意:这里是新增的
        mapDispatchToProps你可以理解成在下面connect的时候提供一个放置好钥匙的函数onIncreaseClick,直接调用就可以去reducer修改state了
    */
    let mapDispatchToProps = (dispatch) =>{
        return{onIncreaseClick:()=>dispatch(increaseAction)}
    }
    /*
        注意:这里是修改了的
        连接你的state和最外层的组件
    */
    let Content = connect(mapStateToProps,mapDispatchToProps)(Index)
    
    let {Component} = React
    
    //使用Provider来把新的App组件打包出来
    class App extends Component{
        render(){
            return <Provider store={store}><Content /></Provider>
        }
    }
    
    export default App
    

    文件位置: app/compoment/index.js

    'use strict'
    
    import React from 'react'
    import { connect } from 'react-redux'
    /*
        注意:这里是修改乐的
        现在不用引入action了,因为前一步已经把钥匙Action放到相应的函数中去,作为props传入组件里面
    */
    //import {increaseAction} from '../redux/action'
    let {Component,PropTypes}  = React
    class Index extends Component{
        //这一步是检查传入的各个prop类型是否正确
        ProTypes = {
            count:PropTypes.number.isRequired,
            onIncreaseClick:PropTypes.func.isRequired,
        }
        constructor(props){
            super(props)
        }
        handleClick(){
            /*
                注意:这里是修改过的
                现在,我们把打包好的,带着钥匙的函数进行调用
            */
            console.log(this.props)
            let {onIncreaseClick} = this.props
            onIncreaseClick()
        }
        render(){
            let {count} = this.props
            return <div onClick = {this.handleClick.bind(this)}  style={styles.circle}>{count}</div>
        }
    }
    //样式文件,不用细看
    ...(以下相同就略去)
    
    

    结语

    redux其实不难理解,按照我个人理解的加工工厂模式:有一家大公司叫做store,里面有工厂(reducer),公司(store)只有一家,但这家公司(store)可以有很多工厂(reducer)。要进去工厂加工产品(state),就得带上相应得钥匙(Action),不同的钥匙(Action)对应工厂中上不同的生产线(redecer里面的switch函数),从而有不同的产出(改变之后的state)//这个在下一步创建

    相关文章

      网友评论

      • 廖志伟:我在看,其实我想知道数据model层在哪写,怎么管理与服务器的数据通讯
        三月懒驴:@廖志伟 数据就是state。和服务器通信要用到中间件。看我第二篇文章:http://www.jianshu.com/p/ddf25ba61982。在Action Creator的阶段获取到数据那么在reducer的阶段就可写入state
      • 一wei渡江:期望楼主再发多一点关于react native的,写的太有用了
        三月懒驴:@一wei渡江 THX~我会继续努力的!
        一wei渡江:@三月懒驴 最近在看这个,发现有用适合的资料不多,你这个现在看比较有收获
        三月懒驴:@一wei渡江 :relieved:有人看才有动力
      • cd381614277e:在创建了store之后怎么将store传递给子级组件 通过 props么
        cd381614277e:@三月懒驴 好的 谢谢
        三月懒驴:@cd381614277e 不需要把store传下去,state和dispatch在connect的时候已经绑到指定组件的props去了,而指定组件下面的组件需要获取state和dispatch的话就通过父组件往下传props吧
      • cd381614277e:index.js 里的
        ProTypes = {
        count:PropTypes.number.isRequired,
        onIncreaseClick:PropTypes.func.isRequired,
        }
        是否有误
        三月懒驴:@cd381614277e 这个是类型检查,正式上线的时候可以不要,如果你这里报了警告就是传入值类型不对
      • cd381614277e:按你的代码 走了一遍 基本理解了 redux 楼主威武
        三月懒驴:@cd381614277e 这个是基础,往下学下去还有几个难点:relieved:

      本文标题:Redux之旅-1

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