[一步一步学react系列] 03—计数器V1.0

前言
在上文中我们学习了react的工作流程,知道了用户点击操作先是分发action,然后reducer根据接收到的action来做具体值的改变的这个曲折过程。
仔细看redux工作流的盆友一定发现了,上文中并没有用到最中心的store,那么下文中我们将引入新的东西:store
知识点:
Provider createStore connect

store类似与vue的vuex,在vue中vuex是一个集中数据和方法的中心仓库,同样,store也有异曲同工之妙。
接下来我们开始真正地接触redux。

安装:

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

以下贴一个使用了store,connect等知识点的同样的计数器例子,只是这次的计数器的值是放在props里公共管理的

/**
 *  @desc redux-example
 */
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'

// UI组件 
/** [1]
 *  两个参数:
 *  value:由props的得到
 *  方法:向外发出action
 */
class Counter extends Component {
  render() {
    const { value, onIncreaseClick } = this.props
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}>Increase</button>
      </div>
    )
  }
}

/**
 *  [2] 定义映射 输入输出逻辑
 *  将UI组件的value值[props]映射到state
 *  将UI组件的方法映射到action 通过dispatch触发action
 */


//将UI组件的props与redux的state映射
function mapStateToProps(state) {
    return {
        value: state.count
    }
}

//将UI组件的props与redux的action映射
function mapDispatchToProps(dispatch) {
    return {
        //用户的onIncreaseClick方法与action映射([3]定义action),通过dispatch触发reducer
        onIncreaseClick: () => dispatch(increaseAction)
    }
}

/**
 *  [3] 给UI组件用connect()方法附上输入输出逻辑[逻辑],生成一个容器组件App
 */
const App = connect(
    mapStateToProps,
    mapDispatchToProps
)(Counter)



/**
 *  [4] 定义action 纯函数 输入什么输出什么 收到的action返回一个type字段
 *   dispatch拿着这个type字段去找reducer,reducer根据不同type匹配不同的动作 并最终返回一个新state
 */

// Action
const increaseAction = { type: 'increase' }


/**
 * [5] 定义reducer 
 *      用户触发什么action对应返回一个经过reducer处理的新state,不同type对应不同的处理方式
 */
// Reducer
function counter(state = { count: 0 }, action) {
  const count = state.count
  switch (action.type) {
    case 'increase':
      return { count: count + 1 }
    default:
      return state
  }
}

/**
 *  [6] 以reducer做为传入值生成一个store对象
 */
// Store
const store = createStore(counter)

/**
 *  [7] 使用Provider组件在根组件外面包一层 App及其子组件就可以拿到state了
 */
ReactDOM.render(
  <Provider store={store}>
    <App /> 
  </Provider>,
  document.getElementById('root')
)

总结:

接上文, 看了代码的可能会惊讶,哇,怎么一下多了这么多东西!
其实我们只是进入了真正的战斗状态而已哈哈。数据的管理思路还是没变。
以下说说此文中我们接触的真正的”战斗状态的redux“:
我们看看同样的计数器”这篇”较前文不同的部分:

|-- mapStateToProps
|-- mapDispatchToProps
|-- App = connect(
        mapStateToProps,
        mapDispatchToProps
    )(Counter)
|-- store = createStore(counter)
|-- <Provider store={store}>
        <App /> 
    </Provider>

细心的盆友一定发现了,本组件Couter没有自身属性了,即state。它的参数和方法都来自于props。如果按照上文的比喻理解,可以认为,小县城连本地仓库和记录本地仓库数据的会计(或笔杆)都没啦!哦漏,现在小县城就仅仅是个”废城”啦,没有自身属性(展示的数据来源于外部),别人给我什么我就展示什么,也会和用户交互但是并不能改变展示的东西,只能发出一个action,传递这个动作的意义让别人来改变组件数据状态。俗称木偶组件

1. 现在理理故事背景:
小县城仍然想得到计算机王国的粮仓储值情况,县城的粮食收益会报告给王国粮食中心,县城的粮食值来源是”王国粮食中心 “。

2. UI组件和容器组件:
我们提到过,现在的小县城是木偶组件了,只负责展示数据和发出动作的action,但是为什么要把小城变为木偶组件?
我们想一下哈,王国有多个县城,多个县城的粮食情况都需要与粮食中心实时,相当于我们将粮食管理控制采取了中央集权制了,让一个完整的组件划分成UI和容器两类,UI专注展示,容器专注逻辑和数据处理。

3. 我们有的“实物”:
1. 一个小县城Couter组件-UI组件(功能:展示数据 + 发出改变数据的action)
2.一个粮仓Store (王国的粮食储备中心,里面实时记录着本国的粮食数量)
3. 一个信号中转站 Reducer (可以实时读取到store的数据及里面生活着一个可以计算的会计师(方法))

我们还需要生成一个县城的容器组件,负责处理县城的数据和逻辑交互,这个组件可以拿到仓库store的数据,同时也可以将UI组件发出的action传给reducer

4.建立联系:
建立县城与粮食中心的联系:

const App = connect(
    mapStateToProps,//将redux的state与UI组件的props映射
    mapDispatchToProps//将redux的action与UI组件的props映射
)(Counter)

说明:
1. store是个仓库,它的state值被下发到各县城组件,作为县城组件的props。即不要被state和props绕晕了
2. connect 奇怪的写法,其实这是个函数式编程写法,在这里可以姑且理解为输入的两个逻辑通过connect被完美地糅合在一起,并携带上counter这个UI组件一起生成了容器组件。

总结:

通过上文我们学习了真实的redux,我们总结下如下几个我认为比较重要的知识点:
0. 容器组件和UI组件
1. state和props区别
2. redux和react关系

笔记详情见:react初学笔记

在回顾了本文学习的redux知识之余,我们跳出redux这个框可以思考如下几个问题:
1.redux的存在意义?数据集中管理方案的其他出路?
2.木偶、容器组件的意义?(我们为什么要将好端端的一个组件分成木偶组件和容器组件?)即为什么推荐无状态组件?

code例子来源:simplest-redux-example

猜你喜欢

转载自blog.csdn.net/young_Emily/article/details/78939642