0%

Redux学习总结

需要解决的问题

“模块(组件)之间需要共享数据”,和“数据可能被任意修改导致不可预料的结果”之间的矛盾。

解决的方法

提高数据修改的门槛

Redux应用场景

  • 随着 JavaScript 单页应用开发日趋复杂,管理不断变化的 state 非常困难

  • Redux的出现就是为了解决state里的数据问题

  • 在React中,数据在组件中是单向流动的

  • 数据从一个方向父组件流向子组件(通过props),由于这个特征,两个非父子关系的组件(或者称作兄弟组件)之间的通信就比较麻烦

    Redux应用场景

Redux设计思想

  • 单一数据源:整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中

  • State 是只读的:唯一改变 state 的方法就是派发(dispatch) action,action 是一个用于描述已发生事件的普通对象

  • 使用纯函数来执行修改:为了描述 action 如何改变 state tree ,你需要编写 reducers

    • Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state
  • 订阅事件:其它组件可以通过订阅(subscribe)store中的状态(state)来刷新自己的视图.

    Redux工作流

Redux概念解析

  • Store

    • 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个Store。

    • Redux 提供createStore这个函数,用来生成Store

      1
      2
      import { createStore } from 'redux';
      const store = createStore(reducer);
  • State

    • Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。当前时刻的 State,可以通过store.getState()拿到
    1
    2
    3
    import { createStore } from 'redux';
    const store = createStore(reducer);
    const state = store.getState();
  • Action

    • 是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。
    • 我们应该尽量减少在 action 中传递的数据。
    • State的变化,会导致View的变化。但是,用户接触不到 State,只能接触到View 所以,State的变化必须是 View导致的。Action 就是 View 发出的通知,表示State 应该要发生变化了。 Action是一个对象。其中的type属性是必须的,表示 Action 的名称。其他属性可以自由设置。
    1
    2
    3
    4
    5
    const ADD_TODO = 'ADD_TODO'
    {
    type: ADD_TODO,
    text: 'Learn Redux'
    }
  • Action Creator

    • View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦。可以定义一个函数来生成 Action,这个函数就叫 Action Creator
    • 在 Redux 中的 action 创建函数只是简单的返回一个 action:
    1
    2
    3
    4
    5
    6
    function addTodo(text) {
    return {
    type: ADD_TODO,
    text
    }
    }
  • store.dispatch

    • View 发出 Action 的唯一方法
    1
    2
    3
    4
    5
    6
    7
    import { createStore } from 'redux';
    const store = createStore(fn);

    store.dispatch({
    type: 'ADD_TODO',
    text: 'Learn Redux'
    });
  • Reducer

    • Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
    • Reducer 是一个纯函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
      • 纯函数
        • 函数的返回结果只依赖于它的参数,相同的输入产生相同的输出
        • 对外层作用域不会产生副作用
    1
    2
    3
    4
    const reducer = function (state, action) {
    // ...
    return new_state;
    };

相关源码分析

createStore

创建数据仓库,store。

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
export default function createStore(reducer, enhancer) {
//处理middleware 中间件
//enhancer:applyMiddleware(...middleware)的执行结果
if (enhancer) {
return enhancer(createStore)(reducer)
}
let state;
let listeners = [];//事件订阅

//返回当前的状态
function getState(){
return state;
}

//订阅事件,用于状态变更,触发view渲染。返回一个取消订阅的方法
function subscribe(listener){
listeners.push(listener);

//取消事件订阅
return function(){
listeners = listeners.filter(function (item){
return item !== listener;
})
}
}

function dispatch(action){
state = reducer(state, action);//获取新的状态
//发布事件 触发view更新
listeners.forEach(function(listener){
listener();
})
}

dispatch({ type: "@@TYEP/REDUX_INIT" });//初始化数据 state赋值为reducer中的默认值

return {
dispatch,
subscribe,
getState,
}
}

bindActionCreator

通过bindActionCreator将 store.dispatch*Action Creator进行组合,降低两者的耦合,同时提高了 store.dispatch 代码的复用。

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
function bindActionCreator(actionCreator, dispatch) {
return function () {
//派发动作
dispatch(actionCreator(arguments));
}
}

//一个辅助方法,能够让我们以方法的形式来派发action
// 如果actionCreators是一个函数,返回一个派发动作的函数
// 如果actionCreators是一个对象,返回一个派发动作的对象
export default function bindActionCreators(actionCreators, dispatch) {
//处理函数
if (typeof actionCreators ==='function'){
return bindActionCreator(actionCreators, dispatch);
}

if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${
actionCreators === null ? 'null' : typeof actionCreators
}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
}

//处理对象
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
//绑定属性:为bindActionCreator返回的函数
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
//返回绑定actionCreators后的对象
return boundActionCreators
}

combineReducers

随着应用变得复杂,需要对 reducer 函数 进行拆分,拆分后的每一块独立负责管理 state 的一部分。

combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore。

合并后的 reducer 可以调用各个子 reducer,并把它们的结果合并成一个 state 对象。state 对象的结构由传入的多个 reducer 的 key 决定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default function combineReducers(reducers) {
return function (state = {}, action) {
let hasChanged = false;//标记state是否更新
const nextState = {};
for (const key in reducers) {
//获取单个reducer 注意:每个reducer中的action type不要重名 否则会触发其他reducer计算新的state 出现非预期的状态更新
const reducer = reducers[key];
const previousStateForKey = state[key];//获取旧状态
const nextStateForKey = reducer(previousStateForKey, action);//计算新状态 action命中会返回新的状态否则返回旧状态
//赋值给nextState对象 合并成一个对象
nextState[key] = nextStateForKey;
//判断state是否修改
hasChanged = hasChanged || previousStateForKey !== nextStateForKey
}
//如果状态没有更新返回旧的状态state 否则返回新的状态
//可以在组件shouldComponentUpdate生命周期 进行状态,属性的是否更新比较 避免不必要的渲染
return hasChanged ? nextState : state;
}
}

参考

-------------本文结束感谢您的阅读-------------