创建Reducer

对于Reducer的创建,同样需要与传统Redux中的Reducer创建方法进行对比。传统定义Reducer都是通过构建一个复杂的大型switch表达式来完成的。

例如延续上一节示例中的Action对应的Reducer,就是以下示例中的样子。

const initialState = { value: 0 };

function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'counter/increase':
      return { value: state.value + action.payload ?? 1 };
    case 'counter/decrease':
      return { value: state.value - action.payload ?? 1 };
    default:
      return state;
  }
}

使用这种传统Reducer的创建方法最大的缺点就是在Reducer要处理Action类型繁多的Action时,switch表达式的结构将变得非常庞大,并且难于管理。所以就更不用说每种Action类型再携带不同形式的载荷的情况了。

Redux Tookit针对Reducer定义时的这种特点,使用构造者模式对其进行了优化。虽然不能明显的减少Reducer所使用的代码量,但是可以让Reducer的定义变得更加清晰。

以下是采用Redux Toolkit提供的createReducer函数完成上面示例中的Reducer创建的示例。

import { createAction, createReducer } from '@reduxjs/toolkit';

interface CounterState {
  value: number;
}

const counterIncreaseBy1 = createAction('counter/increase');
const counterDecreaseBy1 = createAction('counter/decrease');
const counterIncreaseByAmount = createAction<number>('counter/increase');
const counterDecreaseByAmount = createAction<number>('counter/decrease');

// createReducer所接受的第一个参数是State的初始状态,第二个参数是处理传入的不同Action的函数。
// addCase方法主要接受使用createAction创建的Action创建函数作为其匹配条件。
// 除了addCase之外,createReducer还支持addMatcher方法,可以用于使用自定义函数进行匹配。
// addDefaultCase方法通常是用作最后的匹配处理,其不需要任何匹配条件,在之前的所有条件都不匹配时匹配,相当于switch表达式中的default。
const counterReducer = createReducer<CounterState>({ value: 0 }, builder => {
  builder
    .addCase(counterIncreaseBy1, (state, action) => {
      state.value += 1;
    })
    .addCase(counterDecreaseBy1, (state, action) => {
      state.value -= 1;
    })
    .addCase(counterIncreaseByAmount, (state, action) => {
      state.value += action.payload;
    })
    .addCase(counterDecreaseByAmount, (state, action) => {
      state.value -= action.payload;
    })
    .addDefaultCase((state, action) => state));
});

在使用createReducer函数的时候需要注意addCaseaddMatcheraddDefaultCase之间的排列顺序。由于addMatcher能够处理包括action.type在内的附加了其他内容的Action载荷,所以需要放置在仅使用action.type进行判断的addCase之后,但是要放置在不需要任何判断条件的addDefaultCase之前。

另外需要说明的一点是,因为createReducer内部使用了Immer库对数据进行不可变处理,所以在匹配条件的回调函数中可以对State直接进行操作,createReducer内部会将其转换为使用Immer库的操作。

Tip

对于Immer库的使用,本书会在后续章节中进行介绍。