Redux

91 阅读3分钟

Redux 提供的模式和工具使你更容易理解应用程序中的状态何时、何地、为什么、state 如何被更新,以及当这些更改发生时你的应用程序逻辑将如何表现**. Redux 指导你编写可预测和可测试的代码,这有助于你确信你的应用程序将按预期工作。

不止是Redux,所有状态管理工具的目标都是如此。

Redux作为Flux的一个变体,在Flux单向数据流的基础上又多增加了几条准则:

  • 唯一数据源(Single Source of Truth);
  • 保持状态只读(State is read-only);
  • 数据改变只能通过纯函数完成

Store

通过configureStore创建一个store,传入的options的类型中,有必传的reducer属性,有初始化state属性preloadedState

export declare function configureStore(options: ConfigureStoreOptions<S, A, M, E>): EnhancedStore<S, A, M, E>;

export interface ConfigureStoreOptions{
  reducer: Reducer<S, A> | ReducersMapObject<S, A>;
  preloadedState?: PreloadedState<CombinedState<NoInfer<S>>>;
}

export const store = configureStore({
  reducer: {
    counter: counterReducer
  }
});

name为counter的state,对应counterReducer来更新

返回的store类型如下,有几个比较重要的方法:

export interface Store {
  dispatch: Dispatch<A>; 
  getState(): S & StateExt;
  subscribe(listener: ListenerCallback): Unsubscribe;
}

dispatch方法主要做两件事:

  1. 内部根据 action 和 state 调用传入的 reducer 拿到新的state
  2. 循环调用 listener

Action

Redux中的action就是一个简单的object,有唯一的属性type

export type Action<T extends string = string> = {
  type: T
}

Redux内部提供了 UnknownAction 和 AnyAction,通常我们也是使用的也是这两个,在type的基础上挂载额外的payload字段,可以看到额外的字段类型是unknow或者any。

export interface UnknownAction extends Action {
  // Allows any extra properties to be defined in an action.
  [extraProps: string]: unknown
}
export interface AnyAction extends Action {
  // Allows any extra properties to be defined in an action.
  [extraProps: string]: any
}

一个简单的action定义:

const addTodoAction = {
  type: 'todos/todoAdded',
  payload: 'Buy milk'
}

Reducer

reducer是一个函数,函数签名是:(state, action) => newState

Reducer 必需符合以下规则:

  • 仅使用 state 和 action 参数计算新的状态值
  • 禁止直接修改 state。必须通过复制现有的 state 并对复制的值进行更改的方式来做 不可变更新(immutable updates) 。Redux期望所有状态更新都使用不可变的方式。Redux Toolkit 允许我们写可变的逻辑是因为内部用了 Immer 库,会自动帮我吗处理数据的不可变性。
  • 禁止任何异步逻辑、依赖随机值或导致其他“副作用”的代码

Reducer 函数内部的逻辑通常遵循以下步骤:

  • 检查 reducer 是否关心这个 action

    • 如果是,则复制 state,使用新值更新 state 副本,然后返回新 state
  • 否则,返回原来的 state 不变

Dispatch

Redux store 有一个方法叫 dispatch。更新 state 的唯一方法是调用 store.dispatch() 并传入一个 action 对象

store.dispatch({ type: 'counter/increment' })

dispatch 一个 action 可以形象的理解为 "触发一个事件" 。发生了一些事情,我们希望 store 知道这件事。 Reducer 就像事件监听器一样,当它们收到 action 后,它就会更新 state 作为响应。


当我们有多个组件需要共享和使用相同 state时,可能会变得很复杂,尤其是当这些组件位于应用程序的不同部分时。有时这可以通过 "提升 state" 到父组件来解决,但这并不总是有效。

解决这个问题的一种方法是从组件中提取共享 state,并将其放入组件树之外的一个集中位置。这样,我们的组件树就变成了一个大“view”,任何组件都可以访问 state 或触发 action,无论它们在树中的哪个位置!

通过定义和分离 state 管理中涉及的概念并强制执行维护 view 和 state 之间独立性的规则,代码变得更结构化和易于维护。

这就是 Redux 背后的基本思想:应用中使用集中式的全局状态来管理,并明确更新状态的模式,以便让代码具有可预测性。