+
+

Redux中的一些概念

实习第一周,学习学习和学习。
Redux涉及的概念比较多,初学者(比若我)容易摸不清概念之间的关系,所以记录一下。

TODO:

  • [ ] 核对文章中的代码
  • [ ] 补充React-Redux部分
  • [ ] 增加使用Immer重写Reducer的小节

概述

What are Redux and State

Redux is a state management library for JavaScript applications

简单来说,Redux是JS应用的一个状态State管理库。

那么什么是「状态」呢?想象一个简单的场景:用户点击了一个按钮后出现了弹窗。在这个简单的场景中其实就涉及了状态的概念。用JS的对象来描述这个过程,在点击按钮前:

1
2
3
4
const state = {
buttonClicked: false,
modalOpen: false
}

当用户点击按钮后:
1
2
3
4
const state = {
buttonClicked: true,
modalOpen: true
}

在React中,状态被用来存储需要渲染的数据,比如从API获取到的响应等,使用this.setState()就能更新本地组件local components的状态。

What is Redux for

Even an innocent SAP(Single Page App) could grow out of control without clear boundaries between every layer of the application.

状态在JavaScript中无处不在,即便是一个简单的单页面应用也可能有繁杂的状态。

在React中,同级组件之间的通信非常麻烦,常规的解决办法是使用多个中间件middleware进行消息传递,一个组件状态改变后还需要将这个状态传递到所有依赖此状态的其他组件。

-w500

Redux则给出了另一种解决方法,使用单一数据源Store来存储状态数据,所有的组件都可以读取Store或者通过Action修改Store。简而言之,通过Redux,我们可以将多个组件使用的状态拿出来,放入组件树之外的集中位置(顶层容器)。

基本概念

Store

所有的状态state都存放在Store对象中。

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

Reducer

1
2
3
4
5
// src/index.js
import { createStore } from "redux";
import rootReducer from "./reducers/index";

const store = createStore(rootReducer);

从上面的代码可以看出,storecreateStore这个函数接收rootReducer返回的结果。其中,createStore来自引入的Redux库,那么rootReducer又是什么呢?

A reducer is a function that receives the current state and an action object, decides how to update the state if necessary, and returns the new state.

rootReducer是一个reducer,而reducer是一个函数,它接受当前state和一个action,返回新的state。

-w329

比如在本例中,rootReducer接受state后,根据action.type返回不同的state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/reducers/index.js
const initialState = {
todos: []
};

function rootReducer(state = initialState, action) {
switch (action.type) {
case 'ADD_TODO':
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
default: return state
}
};

export default rootReducer;

Rule 2: State是只读的
唯一改变state的方法就是触发action。

你可能已经注意到,在rootReducer中,并没有出现常见的this.setState()方法。这是因为在Redux中,不允许直接修改state,只能表示想要修改的意图。

Rule 3: 使用纯函数执行修改
一个纯函数对一个特定输入总是会返回同样的输出。

在Rule 2中我们知道,state不能被直接修改,所以reducer不能就地修改当前的state,必须先复制一份,返回新的对象作为state。这就要求reducer是个纯函数,也就解释了为什么代码中要使用看起来很复杂的Object.assign()

Action

那代码里面的action又是什么?

action本质上是JS中的对象,其必须包含type字段来表示执行动作的类型。除了type字段外Action对象的结构可以完全自定义,方便传入附加字段,在本例中就增加了一个text字段用来存储待办事项的名称。

当Action的结构过于复杂时,可以使用函数来创建Action:

1
2
3
4
5
6
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}

Dispatch

更新状态的唯一方法是调用store.dispath()并传递一个action对象。我们可以简单地将dispatch理解为触发事件,每当事件触发时,reducer监听到发生的action,就会更新state

1
2
3
function btnClick(text){
dispath(addTodo(text));
}

Redux Flow

  • 初始化
    • const store = createStore(rootReducer);
    • store调用一次`rootReducer,将预设的initialState作为当前state
    • 第一次渲染时,UI组件访问store并使用其中的数据渲染内容。同时,组件也订阅subscribestore这样在store更新时组件都能知道
  • 变动
    • 发生了一些事情,例如用户点击了一个按钮
    • 按钮的handler调用dispatch,指派了一个actionstore
    • store调用一次rootReducer得到新的state
    • store通知所有订阅了自己的UI组件告诉它们状态有更新
    • 每个UI组件检查这次更新是否与自己相关
    • 所有相关组件使用新的数据重新执行一次渲染

React-Redux

Redux是一个JS库,我们可以把它用在纯JS上,或者Angular,或者React。对于React来说,你需要使用react-redux将React组件与Redux Store绑定。

React-Redux提供<Provider />组件,被该组件包裹的内部组件都能访问到store。

通常,我们将整个<App />包裹在<Provider />中:

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

然后使用connect方法生成一个连接到store的组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/containers/AddTodo.js
import { connect } from "react-redux";
import { addTodo } from "./actions";
import { AddTodo } from "./components/AddTodo";

const mapStateToProps = (state, ownProps) => {
return {
todos: state.todos
};
}

const mapDispatchToProps = { addTodo };

export default connect(mapStateToProps, mapDispatchToProps)(AddTodo);

connect函数接收两个可选参数:

  • mapStateToProps: store中数据和组件属性的映射
  • mapDispatchToProps: action和组件属性的映射

参考资料

本文作者: rhinoc

本文链接: https://www.rhinoc.top/redux/

版权声明: 本博客所有文章除特别声明外,均采用BY-NC-SA 4.0国际许可协议,转载请注明。

打赏
Love U 3000
  • Through WeChat
  • Through Alipay
0%