Redux - Getting Started
文章目录

Redux

Redux 基于严格单向数据流实现
反正就是为了打破 React 自身的数据流而创建的

LifeCycle

1. 调用 store.dispatch(action)

  • 可以在任何地方调用这个函数,只要引用 store 进来就可以了.
    • 甚至可以在 AJAX callback 中, 或者 setInterval 里面调用

action 的格式如下:

1
2
3
{type: 'LIKE_ARTICLE', articleId: 42}
{type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary'} }
{type: 'ADD_TODO', text: 'Read the Redux docs.'}

通过这个 action 对于用户操作的描述可以变得非常的具象

2. Reducer 接收 Action

默认会提交两个参数给 Reducer:

  1. 当前 State
  2. 第一步提供给 dispatcher 的参数 action

3. Root Reducer 会将所有 sub reducer 的结果给结合起来并返回

参考下文提到的 combineReducers() 的使用示例

4. Redux 会保存最后结合起来了的 State 并且调用各个监听器

现在这个时候已经获取到了下一个状态了

如果我使用 store.subscribe(listener) 注册了个监听器,会在这个时候被调用,并且可以通过 store.getState() 获取到当前的状态

最后通过更新之后的状态刷新 UI

Key Concepts

  • 使用 Pure Function 来描述 State 变化
    • 即通过各种 Dispatcher 将状态变化的请求提交给 reducer 来处理, reducer 仅仅返回下一个状态

      pure function: 可以根据传入的值预测结果,并且不会影响其他值, 即不改变传入的值也不会创建闭包

    • 因为 reducer 是一个 pure function, 里面不应该对任何状态进行修改
  • 所有的状态变化通过一个大的 Dispatcher 进行分发
    • 因此所有的状态变化都应该提交到这个 Dispatcher 上面, 这样就不会出现不同层次的混乱的数据流了

Reducer

基本格式:

1
(previousState, action) => newState

必须满足以下几点:

  1. 不能够修改传入的值
  2. 不应该提交额外的 API/Http 请求
  3. 调用一些 non-pure function, 比如 Date.now() 或者 Math.random()(这些函数的结果无法预料)
  4. 可以根据传入的参数,确定传回来的值

一个详细一点的例子:

1
2
3
4
5
6
7
8
9
10
export default (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
  • 会根据会根据传入的 action 的类型进行不同的处理
  • 另外 action 不一定要字符串,可以传更多类型,甚至可以传一个 obj 进来
  • 记住一定要返回新的状态, 一定要覆盖到所有的情况

关于多个同级 Reducer

可以通过 combineReducers 函数返回多个 sub-Reducer, 例如上面返回了 ab 两个 reducer

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
import {combineReducers} from 'redux'

const a = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1
default:
return state
}
}

const b = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 2
default:
return state
}
}

const e = combineReducers({
a,
b
})

export default e

如果我们在 reducer 里面提供了多个函数
那么当我们分发一个特定动作的时候, 比如

1
console.log(store.dispatch({type: 'INCREMENT'}) )

这个动作就会提交到所有的函数之上, 也就是说, 上方 a 会接收这个动作,b 也会接受这个动作,然后以下面的格式进行返回:

1
2
3
4
{
a: 1, // 经过 a 处理之后的状态
b: 2 // 经过 b 处理之后的状态
}

类似于用参数 {state, action} 便利并执行了整个 reducer 里面所有 reducer 函数

Store

用于分发 Action 到 Reducers, 他需要做这样的操作

  1. 获取这个应用的状态
  2. 可以通过 getState() 获取到状态
  3. 可以通过 dispatch(action) 来分发动作
  4. 可以通过 subscribe(listener) 来注册监听器
    • 并且还要通过 subscribe(listener) 的返回值决定是否接触监听器

创建 Store 的例子:

1
2
3
import {createStore} from 'redux'****
import todoApp from './reducers'
const store = createStore(todoApp)

最终结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 定一个 reducer
function reducer (state, action) {
/* 初始化 state 和 switch case */
}

// 生成 store, 将 reducer 传进去
const store = createStore(reducer)


// 监听数据变化重新渲染页面
store.subscribe(() => renderApp(store.getState()))

// 首次渲染页面
renderApp(store.getState())

// 后面可以随意 dispatch 了,页面自动更新
store.dispatch(...)

对于 React 的集成

1
2
3
4
5
6
7
8
9
import {Provider, connect} from 'react-redux';

// 在 index 里面将 store 传到 Provider 里面
// 这样在任何 component 里面都可以使用 store 了
render(
<Provider store={store}>
<Content />
</Provider>
);