通过一个计数器来了解react-redux及其用法

react之前在看看的时候工作过,后面到了TX就一直使用vue,react被忘得一干二净,这几天下了一个决心,想把react文档从头到尾重新看一遍,结果发现东西实在是太多了。react,redux,react-redux,react-router...该怎么破?

前几天刚把redux看完,其思想大致了解了下,但是想在react中使用redux结合react-redux是最好的,看了很多文章一直不理解其用法,直到我碰到阮一峰老师写的《Redux 入门教程(三):React-Redux 的用法》才真正理解了,为此写一篇读后感来记录一下,以备日后翻阅

老师的最后提供了一个计数器的demo来展示react-redux的用法,我也是看了这个demo之后才对react-redux的理解更近了一步

demo实现的功能很简单,就是每点击一次按钮计数器加1,整个demo的代码如下:

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'

// UI组件
const Counter = props => {
  const { value, onIncreaseClick } = props
  return (
    <div>
      <span>{value}</span>
      <button onClick={onIncreaseClick}>+</button>
    </div>
  )
}

const mapStateToProps = (state, ownProps) => {
  console.log('mapStateToProps called')
  return {
    value: state.count
  }
}

const mapDispatchToProps = {
  onIncreaseClick: () => {
    return {
      type: 'increase'
    }
  }
}

// 容器组件
const VisibleCounter = connect(mapStateToProps, mapDispatchToProps)(Counter)

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

// store
const store = createStore(reducer)

ReactDOM.render(
  <Provider store={store}>
    <VisibleCounter />
  </Provider>, document.getElementById('root'))
复制代码

下面我们结合react-redux中的概念来具体讲解下

展示组件(UI组件)

react-redux是基于展示组件和容器组件的开发思想,这一点必须明确。

展示组件也叫UI组件,你也可以把它理解为react中的无状态组件,它有以下特征:

  • 无状态,使用this.props
  • 不使用任何redux的API
  • 不带有任何业务逻辑

就像上面的Counter组件就是一个展示组件,它只做展示数据,涉及到的任何业务逻辑都是通过它上层的容器组件实现的

容器组件

容器组件是对对展示组件的包装,有以下特征:

  • 负责管理数据和业务逻辑,不负责UI的展现
  • 带有内部状态state
  • 使用redux的API

总之,记住这个原则就可以了,展示组件负责UI 的呈现,容器组件负责管理数据和逻辑

connect

react-redux提供的connect方法用于从UI组件生成容器组件,像下面这样

const VisibleCounter = connect(mapStateToProps, mapDispatchToProps)(Counter)
复制代码

mapStateToProps负责输入逻辑,将state映射到UI组件的参数(props)

mapDispatchToProps负责输出逻辑,将用户对UI组件的操作映射成Action

mapStateToProps

一个函数,建立一个从外部state对象到UI组件props对象的映射关系

mapStateToProps执行后返回一个对象

const mapStateToProps = (state, ownProps) => {
  return {
    value: state.count
  }
}
复制代码

mapStateToProps有两个参数,第一个是state,第二个是当前的props

每当state更新的时候,mapStateToProps会自动执行,计算出UI组件的props,从而触发UI组件的重新渲染

mapDispatchToProps

mapDispatchToProps用来建立UI组件的props到dispatch方法的映射,它定义了用户的哪些操作会生成action,传给store,它可以是一个函数或者一个对象

当它是一个函数,有两个参数,第一个是dispatch,第二个是当前props

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        onIncreaseClick: () => {
            dispatch({ type: 'increase' })
        }
    }
}
复制代码

当它是一个对象

const mapDispatchToProps = {
    onIncreaseClick: () => {
        return {
            type: 'increase'
        }
    }
}
复制代码

从以上两种类型可以知道,当mapDispatchToProps是函数时,应该返回一个对象,键值对应UI组件触发某个事件的回调函数名,键值是一个函数,里面调用dispatch来发出一个action

当mapDispatchToProps是一个对象时,键值对应UI组件触发某个事件的回调函数名,键值是一个函数(Action Creator),返回一个action,该action会由react-redux自动调用dispatch

Provider

方便我们的每个子组件拿到state对象,不然一级一级传递state组件会非常麻烦,它的使用方法和一般的组件使用方法一样,它有一个store属性,而我们的store是调用redux提供的createStore方法生成的

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

// store
const store = createStore(reducer)

ReactDOM.render(
  <Provider store={store}>
    <VisibleCounter />
  </Provider>, document.getElementById('root'))
复制代码

关于Provider组件实现的原理是通过React组件的context属性,下面是我利用context属性自己实现的Provider组件

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'

// UI组件
const Counter = props => (
  <div>
    <span>{props.value}</span>
    <button onClick={props.onIncreaseClick}>+</button>
  </div>
)

// 中间件
const MiddleVisibleCounter = () => (
  <Consumer>
    { ({store}) => <VisibleCounter store={store} /> }
  </Consumer>
)

// 容器组件
class VisibleCounter extends Component {
  constructor(props) {
    super(props)
    const { store } = this.props
    store.subscribe(() => {
      this.setState(store.getState())
    })
    this.state = store.getState()
  }
  dispatchIncreaseAction() {
    const { store } = this.props
    store.dispatch({ type: 'increase' })
  }
  render() {
    return (
      <Counter value={this.state.count} onIncreaseClick={() => this.dispatchIncreaseAction()} />
    )
  }
}

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

// store
const store = createStore(reducer)

const { Provider: Provider2, Consumer } = React.createContext({
  store: store
})

const Provider = (props) => {
  return (
    <Provider2 value={{store:props.store}}>
      { props.children }
    </Provider2>
  )
}

ReactDOM.render(
  <Provider store={store}>
    <MiddleVisibleCounter />
  </Provider>
  , document.getElementById('root'))
复制代码

最后

你们的打赏是我写作的动力


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值