redux同步使用学习

传统mvc 模型


model 与 view 之间的双向对话, 让数据和状态管理变的复杂

Redux (Flux + reducer)

基于Flux 单向数据流的状态管理框架

  1. 单向数据流
  2. 唯一数据源 (Store)
  3. 保持状态只读
  4. 数据改变只能通过纯函数完成(Reducer)

Action

  • 行为的抽象
  • 普通的JS 对象
  • 必须有一个type
  • 一般由方法生成
1
2
// Action
const increaseAction = { type: 'increase' }

Reducer

响应的抽象,只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。 (previousState, action) => newState

1
2
3
4
5
6
7
8
9
10
11
// Reducer
function counter(state = { count: 0 }, action) {
const count = state.count
switch (action.type) {
case 'increase':
return { count: count + 1 }
default:
return state
}
}
export default todos

Store

状态树, 状态的“数据库”,
Store 有以下职责:

  • 维持应用的 state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器

1
2
// Store
const store = createStore(counter)

container 与 component 区分

component: UI组件

不含有状态,UI 组件又称为”纯组件”,即它纯函数一样,纯粹由参数决定它的值

container: 容器组建

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

connect 用于从UI组件生成容器组件

connect方法接受两个参数:mapStateToProps和mapDispatchToProps

  • (1)输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数
  • (2)输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。

mapStateToProps()

mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。


1
2
3
4
5
6
// Map Redux state to component props
function mapStateToProps(state) {
return {
value: state.count
}
}

connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。


mapDispatchToProps()

mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。

1
2
3
4
5
6
// Map Redux actions to component props
function mapDispatchToProps(dispatch) {
return {
onIncreaseClick: () => dispatch(increaseAction)
}
}

provider 组件

connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。
一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。React-Redux 提供Provider组件,可以让容器组件拿到state

1
2
3
<Provider store={store}>
<App />
</Provider>

实例


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'

// React component
class Counter extends Component {
render() {
const { value, onIncreaseClick } = this.props
return (
<div>
<span>{value}</span>
<button onClick={onIncreaseClick}>Increase</button>
</div>
)
}
}

Counter.propTypes = {
value: PropTypes.number.isRequired,
onIncreaseClick: PropTypes.func.isRequired
}

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

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

// Store
const store = createStore(counter)

// Map Redux state to component props
function mapStateToProps(state) {
return {
value: state.count
}
}

// Map Redux actions to component props
function mapDispatchToProps(dispatch) {
return {
onIncreaseClick: () => dispatch(increaseAction)
}
}

// Connected Component
const App = connect(
mapStateToProps,
mapDispatchToProps
)(Counter)

ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)