Redux
一个可以预测(Predictable)的State容器,Redux 不允许对状态直接进行修改,而是要求使用 Action(一个 JavaScript 对象)来描述状态的变化,Reducer 接收 Action 然后计算生成一个新的State替换旧的 State。每个 Action 都是可以记录和回溯的,所以相同的 Action 和相同的顺序就可以保证最终State的一致性,这就是所谓的 Predictable。
Action
Action 是一个原始的 JavaScript 对象,用来描述发生的事件,类似 Event。
每个 Action 都有一个type字段,type的类型是字符串,用来给 Action 命名,type通常以domain/eventName形式定义。第一部分标识 action 的分类或特性,第二部分定义发生的事件。
除了type外每个 action 还可以包含其他字段用来添加额外信息,通常我们使用payload进行聚合。
const addTodoAction = {
type: 'todos/todoAdded',
payload: 'Buy milk'
}
reducer
reduce 是一个纯函数,形式如(state, action) => newState,接收当前的state和 action,根据 action 的 type 和其它属性,计算生成一个新的state并返回。
注意:
- reducer 函数必须是一个纯函数,只有这样才能保证 Redux 的可预测性。
- 在 reducer 内不能够直接修改 state,而是使用 immutable updates 返回一个新的 state,直接修改
state会导致难以定位的 bug。 - reducer 函数不能是异步的,因为异步函数的执行顺序无法保证,会破坏 redux 的可预测性。
在实际项目中我们需要写很多的 reducer,这时我们会根据业务逻辑进行拆分,然后另外写一个 reducer 来整合。
function todoApp(state = {}, action) {
return {
todos: todos(state.todos, action),
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
};
}
Store & Dispatch
当前 redux 程序的state寄生对象,有store.getState()和store.dispatch()方法
state 只能包含object、array和基本类型。TODO:why?
Dispatch 是更新 reduxstate的唯一方法,store 在接收到 dispatch 的 action 后会运行 reducer 方法并使用 reducer 产生的新state替换旧 state。
Selector & Action Creators
在使用 redux 的过程中我们需要写很多 action 创建 和state获取的相关的代码,通常我们会对这两类代码进行封装,创建 action 对象的方法叫 Action Creator,获取state的函数叫 Selector。
Action Creator 函数接收 payload 信息,返回 action 对象。这样我们可以避免每次都手写 action,保证 action 的准确性。
Selector 封装可以隔离 UI 和 Store,在 Store 修改时只需要修改 Selector,无需对每一个 UI 进行修改。
const addTodoActionCreator = text => {
return {
type: 'todos/todoAdded',
payload: text
}
}
const counterValueSelector =`state`=> state.value
const currentValue = counterValueSelector(store.getState())
API
Redux 的 API 非常简单,主要有以下四个
createStore(reducer):创建 storestore.subscribe(callback):订阅变化store.getState():获取 statestore.dispatch(action):触发 action
import { createStore } from "redux";
// 初始state值
const initState = {
count: 0,
};
// reducer
function reducer(state = initState, action) {
switch (action.type) {
case "incremented":
return {
value: state.value + 1,
};
case "decremented":
return {
value: state.value - 1,
};
default:
return state;
}
}
// 创建store
let store = createStore(reducer);
// 订阅变化
store.subscribe(() => {
// 获取store
console.log(store.getState());
});
// 触发action
store.dispatch({ type: "incremented" }); // {value: 1}
store.dispatch({ type: "decremented" }); // {value: 0}
Redux DevTools
immer
redux 期望全部的state更新都是 immutability,修改state会引起奇怪的 bug,使程序难以理解和测试。
对于数组、对象等引用类型变量进行 immutability 操作非常繁琐切容易出错,immer可以让我们更容易的进行 immutability 编程。
Middleware
redux middleware 可以用于对dispatch方法进行定制,允许dispatch更灵活的action,例如函数或者 promise 等,在中间里可以执行一些副作用,例如异步请求、日志打印等。
redux middleware 执行是在 action 派发到 reducer 接收之前,使view -→> action -> reducer -> store转变为view -> action -> middleware -> reducer -> store

redux middleware 是一个函数,接收dispatch和getState两个参数。
function exampleMiddleware(storeAPI) {
const { dispatch, getState } = storeAPI;
// next:下一个middleware
return function wrapDispatch(next) {
return function handleAction(action) {
return next(action)
}
}
}
扩展 store,允许执行:
- 执行额外的逻辑,例如日志记录
- 暂停、修改、延迟、替换或者取消 action
- 增强
dispatch如何接受处理除了基本的对象和原始类型数据之前的数据类型,例如函数或者 Promise 对象。 - 添加其它额外的 code
redux-thunk
redux-thunk 允许dispatch一个函数,这个函数就被称为thunk,thunk函数有dispatch和getState两个参数,可以在thunk内执行异步逻、调用dispatch和getState方法,获取当前state或触发一个action,通常是触发一个同步action。
为了保持一致性,我们通常也会为thunk函数也创建一个 creator 函数,可以在这个函数传入合适的业务参数。
export const incrementAsync = (amount) => {
return (dispatch, getState) => {
setTimeout(() => {
dispatch(incrementByAmount(amount))
}, 1000)
}
}
store.dispatch(incrementAsync(5))
```JS redux-thunk 源码 function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => (next) => (action) => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); }
return next(action); // next 代表是的这 action 直接往下一个 middleware 走,假如没有就进去reducer
};
}
const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
## TODO:Redux-saga
## [Reselect](https://github.com/reduxjs/reselect)
Reselect 是一个用来实现 memoized selector functions 的工具库。
Memoization 指的是记录上一次的输入和返回,在下一次执行的时候对比两次的输入,如果相同那么就直接返回上一次的记录的返回。
redux 的每个 action 都会触发选择器 re-run,如果组件引用的`state`发生变化,那么组件就会 re-render。memoized selector 可以避免没必要的 re-render。
## Normalized`State`Structure
我们经常有需要在一个长列表中找到某一特定项目的需求场景,如果这个列表非常长,这个操作将会是一个特别耗时的操作。我们可以对数据进行 Normalizing。
Normalizing 是指加工原始数据生成一个对象,包括一个 ID 数组,一个以 ID 为 key 的 lookup tables。
```JavaScript
// 原数据
const DataArr = [
{id: "user1", firstName, lastName},
{id: "user2", firstName, lastName},
{id: "user3", firstName, lastName},
]
// Normalized Data
const NormalizedData = {
users: {
ids: ["user1", "user2", "user3"],
entities: {
"user1": {id: "user1", firstName, lastName},
"user2": {id: "user2", firstName, lastName},
"user3": {id: "user3", firstName, lastName},
}
}
}