reudx的流程解析

301 阅读9分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

reudx的标准流程:

View在redux中会派发action方法;

action通过store的dispatch方法会派发给store;

store接收action,连同之前的state,一起传递给reducer;

reducer返回新的数据给store;

store去改变自己的state

一、Action

1、Action描述发生了什么,store 的唯一数据来源,一般通过 store.dispatch() 将action传到store。

2、Action创建函数

Redux 中的 action 创建函数只是简单的返回一个 action:

image.png

3、发起一次dispatch 过程

1)Redux 中只需把 action 创建函数的结果传给 dispatch() 方法即可:

dispatch(addTodo(text))

2)创建一个被绑定的Action创建函数,自动dispatch

创建:

const boundAddTodo = text => dispatch(addTodo(text))

调用:

boundAddTodo(text)

3)store.dispatch()

store 里能直接通过 store.dispatch() 调用 dispatch() 方法,但是多数情况下会使用 react-redux 提供的 connect() 帮助器来调用。

bindActionCreators()可以自动把多个 action 创建函数 绑定到 dispatch() 方法上。

二、reducer

1、基本结构

(一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数叫做纯函数。

为什么要煞费苦心地构建纯函数?因为纯函数非常“靠谱”,执行一个纯函数你不用担心它会干什么坏事,它不会产生不可预料的行为,也不会对外部产生影响。不管何时何地,你给它什么它就会乖乖地吐出什么。如果你的应用程序大多数函数都是由纯函数组成,那么你的程序测试、调试起来会非常方便。)

reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。

(previousState, action) => newState

2、combineReducers()工具类

Redux 提供了一个combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。

以下的代码通过combineReducers方法将三个子 Reducer 合并成一个大的函数。

image.png

该工具类只是生成一个函数,这个函数来调用你的一系列 reducer,将多个reducer合并成为一个,每个 reducer 根据它们的 key 来筛选出 state 中的一部分数据并处理,然后这个生成的函数再将所有 reducer 的结果合并成一个大的对象。正如其他 reducers,如果 combineReducers() 中包含的所有 reducers 都没有更改 state,那么也就不会创建一个新的对象。

三、store

1、Store 有以下职责:

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

2、创建

根据已有的reducer来创建,利用createStore(),传入多个reducer合并的reducer,它的的第二个参数是可选的,可设置state的初始状态。

let store = createStore(reducers)

四、数据流

设计核心:严格的单项数据流

好处:应用中所有的数据都遵循相同的生命周期,使应用变得更加可预测且容易理解。同时也鼓励做数据范式化,这样可以避免使用多个且独立的无法相互引用的重复数据。

1、redux 应用数据的生命周期

1)调用 store.dispatch(action)

Action就是一个描述“发生了什么”的普通对象。比如:

image.png

2)Redux store 调用传入的 reducer 函数

Store 会把两个参数传入 reducer,当前的 state 树和 action

3)根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树

Redux 原生提供 combineReducers() 辅助函数,来把根 reducer 拆分成多个函数,用于分别处理 state 树的一个分支。

4)Redux store 保存了根 reducer 返回的完整 state 树

这个新的树就是应用的下一个 state!所有订阅 store.subscribe(listener) 的监听器都将被调用;监听器里可以调用 store.getState()获得当前 state

五、展示组件和容器组建

image.png

展示组件:

  • 主要负责组件内容如何展示
  • 从props接收父组件传递来的数据
  • 大多数情况可以通过函数定义组件声明

容器组件:

  • 主要关注组件数据如何交互
  • 拥有自身的state,从服务器获取数据,或与redux等其他数据处理模块协作
  • 需要通过类定义组件声明,并包含生命周期函数和其他附加方法

好处:

  • 解耦了界面和数据的逻辑
  • 更好的可复用性,比如同一个回复列表展示组件可以套用不同数据源的容器组件
  • 利于团队协作,一个人负责界面结构,一个人负责数据交互

connect:它的作用是将组件和models结合在一起。将models中的state绑定到组件的props中。并提供一些额外的功能,譬如dispatch。connect 方法返回的也是一个 React 组件,通常称为容器组件。因为它是原始 UI 组件的容器,即在外面包了一层 State。

connect 的作用机制:

  • 将数据模型中的 state 映射到组件 props
  • 将数据模型中的 reducer 或 effect 以 dispatch(action) 的形态映射到组件 props
  • 当数据模型中的 state 发生变化时,通知关联的组件进行更新

可以通过connect()方法,容器组件可以把这些展示组件和 Redux 关联起来,

  • 用 connect() 前,需要先定义 mapStateToProps 这个函数来指定如何把当前 Redux store state 映射到展示组件的 props 中;
  • 除了读取 state,容器组件还能分发 action。类似的方式,可以定义 mapDispatchToProps() 方法接收 dispatch()方法并返回期望注入到展示组件的 props 中的回调方法。

image.png

六、mapStateToProps && mapDispatchToProps

image.png

七、中间件

1)Redux middleware:它提供的是位于 action 被发起之后,到达 reducer 之前的扩展点,

其实每一个middleware都在增强dispatch的功能,在dispatch action的前后搞点事情。

2)applyMiddleware()

applyMiddleware是一个高阶函数,它将中间件装饰到了createStore函数上。

作用是将所有中间件组成一个数组,依次执行。

applayMiddleware 函数接受这样的参数

applyMiddleware(thunkMiddleware,middleware1,middleware2,...)(createStore);

它返回一个新的createStore函数

八、可记忆的reselect函数

1、作用:在Reselect中间中使用了缓存机制,在组件交互的时候,state发生变化时,减轻渲染的压力。

createSelector(…inputSelectors|[inputSelectors],resultFunc)

Reselect 提供 createSelector 函数来创建可记忆的 selector。createSelector 接收一个 input-selectors 数组和一个转换函数作为参数。如果 state tree 的改变会引起 input-selector 值变化,那么 selector 会调用转换函数,传入 input-selectors 作为参数,并返回结果。如果 input-selectors 的值和前一次的一样,它将会直接返回前一次计算的数据,而不会再调用一次转换函数。

image.png getVisibilityFilter 和 getTodos 是 input-selector。因为他们并不转换数据,所以被创建成普通的非记忆的 selector 函数。但是,getVisibleTodos 是一个可记忆的 selector。他接收 getVisibilityFilter 和 getTodos 为 input-selector,还有一个转换函数来计算过滤的 todos 列表。

2、连接 Selector 和 Redux Store

image.png

九、React-redux——>

是由 React Redux 提供的高阶组件,用来让你将 Redux 绑定到 React。

其包裹的组件都可以访问到store

image.png

十、技巧

1、使用扩展运算符...

1)object.assign()

示例中, Object.assign() 将会返回一个新的 state 对象, 而其中的 visibilityFilter 属性被更新了,

但 Object.assign() 冗长的写法会迅速降低 reducer 的可读性。

image.png 2)...

通过扩展运算符(...)替换,可以简化写法

image.png

十一:组织Reducer

1、Reducer 基本结构

一个单一的 reducer 最终需要做以下几件事:

  • reducer 第一次被调用的时候,state 的值是 undefined。reducer 需要在 action 传入之前提供一个默认的 state 来处理这种情况。
  • reducer 需要先前的 state 和 dispatch 的 action 来决定需要做什么事。
  • 假设需要更改数据,应该用更新后的数据创建新的对象或数组并返回它们。
  • 如果没有什么更改,应该返回当前存在的 state 本身。

image.png

image.png

2、逻辑拆分

  • reducer: 任何符合 (state, action) -> newState 格式的函数(即,可以用做 Array.reducer 参数的任何函数)。
  • root reducer: 通常作为 createStore 第一个参数的函数。他是唯一的一个在所有的 reducer 函数中必须符合 (state, action) -> newState 格式的函数。
  • slice reducer: 一个负责处理状态树中一块切片数据的函数,通常会作为 combineReducers 函数的参数。
  • case function: 一个负责处理特殊 action 的更新逻辑的函数。可能就是一个 reducer 函数,也可能需要其他参数才能正常工作。
  • higher-order reducer: 一个以 reducer 函数作为参数,且/或返回一个新的 reducer 函数的函数(比如: combineReducers, redux-undo)。

3、combineReducers 用法

使用了 ES6 中的对象字面量简写方式,在最后的 state 中 key 的名字和 import 进来的变量的名字一样,这可能会造成一定的干扰。

image.png 建议对其进行重命名,如下方式

image.png (思考)

为什么要传入一个旧的state,返回一个新的state,而不是直接修改旧state里想修改的某个值?

因为为了方便可追溯,

redux-devtools中,我们可以查看到redux下所有通过reducer更新state的记录,每一个记录都对应着内存中某一个具体的state,让用户可以追溯到每一次历史操作产生与执行时,当时的具体状态,这也是使用redux管理状态的重要优势之一.

若不创建副本,redux的所有操作都将指向内存中的同一个state,我们将无从获取每一次操作前后,state的具体状态与改变,若没有副本,redux-devtools列表里所有的state都将被最后一次操作的结果所取代.我们将无法追溯state变更的历史记录.

创建副本也是为了保证向下传入的this.props与nextProps能得到正确的值,以便我们能够利用前后props的改变情况以决定如何render组件。