Redux详解

Redux是一个很好的状态管理库,提出了单向数据流、中间件等概念。Redux三大核心概念是:state(状态)、action以及reducer(根据旧 state 和 action 来计算新 state),并且限制只能使用纯函数来对状态进行修改。

Redux设计原则

单一数据源

整个应用的 全局 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

状态不可变

state 是只读的,只能通过触发 action 来改变状态。

使用纯函数

Reducer 是纯函数。

什么是纯函数?

  1. 如果函数的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化,必须只依赖于其输入参数。

  2. 该函数不会产生任何可观察的副作用。

createStore

redux 通过createStore方法来创建 store,它很好地遵循了上述的redux的设计原则,即单一数据源、状态不可变,并且可以通过store enhancer和中间件来提高可扩展性

export function createStore<
  S, // state
  A extends Action, // action
  Ext extends {} = {}, // 增强器
  StateExt extends {} = {}, // 状态扩展
  PreloadedState = S // 预加载状态
>(
  reducer: Reducer<S, A, PreloadedState>,
  preloadedState?: PreloadedState | StoreEnhancer<Ext, StateExt> | undefined,
  enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext {
  ... // 参数检查

  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState as StoreEnhancer<Ext, StateExt>
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    // 如果提供了增强器,就用它来包装 createStore 函数,并且传入 reducer 和 preloadedState
    return enhancer(createStore)(
      reducer,
      preloadedState as PreloadedState | undefined
    )
  }
  // 初始化当前的 reducer 和 currentState
  let currentReducer = reducer
  let currentState: S | PreloadedState | undefined = preloadedState as
    | PreloadedState
    | undefined
  let currentListeners: Map<number, ListenerCallback> | null = new Map()
  let nextListeners = currentListeners
  let listenerIdCounter = 0
  let isDispatching = false

  // 使用 Map 来管理监听器,每个 listener 对应唯一的 id
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = new Map()
      currentListeners.forEach((listener, key) => {
        nextListeners.set(key, listener)
      })
    }
  }

  // 返回当前状态树
  function getState(): S {
    return currentState as S
  }

  // 添加一个监听器,在每次 dispatch 时被调用,通过 getState 获取当前状态
  function subscribe(listener: () => void) {
    let isSubscribed = true

    ensureCanMutateNextListeners()
    const listenerId = listenerIdCounter++
    nextListeners.set(listenerId, listener)

    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      nextListeners.delete(listenerId)
      currentListeners = null
    }
  }

  // 调用 reducer 并更新状态,并通知所有监听器
  function dispatch(action: A) {

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    const listeners = (currentListeners = nextListeners)
    listeners.forEach(listener => {
      listener()
    })
    return action
  }
  // 替换当前的 reducer,在动态加载 reducer 和热重载时很有用
  function replaceReducer(nextReducer: Reducer<S, A>): void {
    
    currentReducer = nextReducer as unknown as Reducer<S, A, PreloadedState>
    
    dispatch({ type: ActionTypes.REPLACE } as A)
  }

  // 提供与 observable/reactive 库的互操作性
  // 返回一个最小的 observable,用于监听状态变化
  function observable() {
    const outerSubscribe = subscribe
    return {
      subscribe(observer: unknown) {

        function observeState() {
          const observerAsObserver = observer as Observer<S>
          if (observerAsObserver.next) {
            observerAsObserver.next(getState())
          }
        }

        observeState()
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this
      }
    }
  }
  // 创建 store 时,自动 dispatch 一个 INIT 动作,确保所有 reducer 返回初始状态。
  dispatch({ type: ActionTypes.INIT } as A)
  // 返回 store 对象,向外暴露 dispatch, subscribe, getState, replaceReducer 等 API
  const store = {
    dispatch: dispatch as Dispatch<A>,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  } as unknown as Store<S, A, StateExt> & Ext
  return store
}

Enhancer

在 Redux 中,store enhancer 是一种高级功能,它允许你在创建 store 时对 store 的行为进行修改。它本质上就是在createStore上额外包裹了一层,可以对store上暴露的dispatchgetStatesubscribe进行重写。

下面是一个基本的例子:

import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

function loggerEnhancer(createStore) {
  return function (reducer, preloadedState, enhancer) {
    const store = createStore(reducer, preloadedState, enhancer);

    const dispatch = store.dispatch;
    store.dispatch = function (action) {
      console.log('Dispatching action:', action);
      return dispatch(action);
    };

    return store;
  };
}

const store = createStore(
  rootReducer,
  undefined,
  applyMiddleware(thunk),
  loggerEnhancer
);

export default store;

Middleware

Redux 推荐使用中间件来扩展功能,比如通过包装dispatch方法来实现需求。并且middleware是可组合的。多个 middleware 可以被组合到一起使用,形成 middleware 链。

export default function applyMiddleware(
  ...middlewares: Middleware[]
): StoreEnhancer<any> {
  return createStore => (reducer, preloadedState) => {
    const store = createStore(reducer, preloadedState)
    let dispatch: Dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }

    const middlewareAPI: MiddlewareAPI = {
      getState: store.getState,
      dispatch: (action, ...args) => dispatch(action, ...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

这段代码的重点是compose函数,它通过数组 reduce 方法,将传入的多函数参数,进行函数柯里化处理,然后逐个执行函数。

export default function compose(...funcs: Function[]) {
  if (funcs.length === 0) {
    return <T>(arg: T) => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce(
    (a, b) =>
      (...args: any) =>
        a(b(...args))
  )
}

Redux Toolkit

Redux Toolkit(简称RTK)是官方推荐的Redux写法。@reduxjs/toolkit包封装了redux包的核心能力,简化了redux的使用。

Redux Toolkit有两个关键的API,configureStorecreateSlice

configureStore

configureStore接收 reducer、中间件、增强器、预加载状态和 devtools 作为参数,进行一系列处理,最终调用createStore方法创建一个配置完善的 store。

export function configureStore<
  S = any, // state的类型
  A extends Action = UnknownAction, // action 的类型
  M extends Tuple<Middlewares<S>> = Tuple<[ThunkMiddlewareFor<S>]>, // 中间件元组的类型
  E extends Tuple<Enhancers> = Tuple<
    [StoreEnhancer<{ dispatch: ExtractDispatchExtensions<M> }>, StoreEnhancer]
  >, // 增强器元组的类型
  P = S, // 预加载状态的类型
>(options: ConfigureStoreOptions<S, A, M, E, P>): EnhancedStore<S, A, E> {
  // 构建默认的中间件列表,通常包括 redux-thunk 中间件。可以通过 middleware 参数进行自定义。
  const getDefaultMiddleware = buildGetDefaultMiddleware<S>()

  const {
    reducer = undefined,
    middleware,
    devTools = true,
    preloadedState = undefined,
    enhancers = undefined,
  } = options || {}

  let rootReducer: Reducer<S, A, P>
  // 处理 reducer
  if (typeof reducer === 'function') {
    rootReducer = reducer // 如果是函数,直接使用
  } else if (isPlainObject(reducer)) {
    // 如果是对象,就创建根 reducer
    rootReducer = combineReducers(reducer) as unknown as Reducer<S, A, P>
  } else {
    throw new Error(
      '`reducer` is a required argument, and must be a function or an object of functions that can be passed to combineReducers',
    )
  }

  if (!IS_PRODUCTION && middleware && typeof middleware !== 'function') {
    throw new Error('`middleware` field must be a callback')
  }
  // 中间件处理
  let finalMiddleware: Tuple<Middlewares<S>>
  if (typeof middleware === 'function') {
    // 如果是函数,调用这个函数并且传入默认中间件列表
    finalMiddleware = middleware(getDefaultMiddleware)
    // 检查中间件列表是否有效
    if (!IS_PRODUCTION && !Array.isArray(finalMiddleware)) {
      throw new Error(
        'when using a middleware builder function, an array of middleware must be returned',
      )
    }
  } else {
    // 不是函数,使用默认中间件列表
    finalMiddleware = getDefaultMiddleware()
  }

  let finalCompose = compose
  // 集成 devtools
  if (devTools) {
    finalCompose = composeWithDevTools({
      trace: !IS_PRODUCTION,
      ...(typeof devTools === 'object' && devTools),
    })
  }
  // 增强器处理
  const middlewareEnhancer = applyMiddleware(...finalMiddleware)

  const getDefaultEnhancers = buildGetDefaultEnhancers<M>(middlewareEnhancer)

  let storeEnhancers =
    typeof enhancers === 'function'
      ? enhancers(getDefaultEnhancers) // 调用增强器函数并传入默认增强器列表
      : getDefaultEnhancers() // 不是函数就用默认增强器列表

  const composedEnhancer: StoreEnhancer<any> = finalCompose(...storeEnhancers)
  // 创建 store,传入根reducer、预加载状态和组合后的增强器
  return createStore(rootReducer, preloadedState as P, composedEnhancer)
}

createSlice

让你使用 Immer 来编写 reducer,可以使用 "mutating" JS 语法,比如 state.value = 123,不需要使用拓展运算符。内部基于你的 reducer 名称生成 action type 字符串。并且它能很好地兼容 TS。

function createSlice<
  State, // 状态
  CaseReducers extends SliceCaseReducers<State>, // reducer
  Name extends string, // name
  Selectors extends SliceSelectors<State>, // slice selector
  ReducerPath extends string = Name, // reducer 路径
>(
  options: CreateSliceOptions<
    State,
    CaseReducers,
    Name,
    ReducerPath,
    Selectors
  >,
): Slice<State, CaseReducers, Name, ReducerPath, Selectors> {
  const { name, reducerPath = name as unknown as ReducerPath } = options
  // reducers 处理
  // 如果是函数,就调用这个函数并传入 buildReducerCreators
  // 否则直接使用 reducers 对象
  const reducers =
    (typeof options.reducers === 'function'
      ? options.reducers(buildReducerCreators<State>())
      : options.reducers) || {}

  const reducerNames = Object.keys(reducers)
  // 创建 context,后面为每个 reducer 创建一个类型和名称,存储在 context 中
  const context: ReducerHandlingContext<State> = {
    sliceCaseReducersByName: {},
    sliceCaseReducersByType: {},
    actionCreators: {},
    sliceMatchers: [],
  }
  // 上下文方法
  const contextMethods: ReducerHandlingContextMethods<State> = {
    // 添加 case reducers
    addCase(
      typeOrActionCreator: string | TypedActionCreator<any>,
      reducer: CaseReducer<State>,
    ) {
      const type =
        typeof typeOrActionCreator === 'string'
          ? typeOrActionCreator
          : typeOrActionCreator.type
      
      context.sliceCaseReducersByType[type] = reducer
      return contextMethods
    },
    // 添加匹配器
    addMatcher(matcher, reducer) {
      context.sliceMatchers.push({ matcher, reducer })
      return contextMethods
    },
    exposeAction(name, actionCreator) {
      context.actionCreators[name] = actionCreator
      return contextMethods
    },
    exposeCaseReducer(name, reducer) {
      context.sliceCaseReducersByName[name] = reducer
      return contextMethods
    },
  }
  // 处理 reducer 定义
  reducerNames.forEach((reducerName) => {
    const reducerDefinition = reducers[reducerName]
    const reducerDetails: ReducerDetails = {
      reducerName,
      type: getType(name, reducerName),
      createNotation: typeof options.reducers === 'function',
    }
    // 区分异步 thunk 和普通 reducer 的定义
    if (isAsyncThunkSliceReducerDefinition<State>(reducerDefinition)) {
      handleThunkCaseReducerDefinition(
        reducerDetails,
        reducerDefinition,
        contextMethods,
        cAT,
      )
    } else {
      handleNormalReducerDefinition<State>(
        reducerDetails,
        reducerDefinition,
        contextMethods,
      )
    }
  })
  // 构建最终的 reducer
  function buildReducer() {
    const [
      extraReducers = {},
      actionMatchers = [],
      defaultCaseReducer = undefined,
    ] =
      typeof options.extraReducers === 'function'
        ? executeReducerBuilderCallback(options.extraReducers)
        : [options.extraReducers]
    // 结合额外的 reducers、action matchers和默认的case reducer
    const finalCaseReducers = {
      ...extraReducers,
      ...context.sliceCaseReducersByType,
    }
    // 调用 createReducer 来创建新的 reducer
    return createReducer(options.initialState, (builder) => {
      for (let key in finalCaseReducers) {
        builder.addCase(key, finalCaseReducers[key] as CaseReducer<any>)
      }
      for (let sM of context.sliceMatchers) {
        builder.addMatcher(sM.matcher, sM.reducer)
      }
      for (let m of actionMatchers) {
        builder.addMatcher(m.matcher, m.reducer)
      }
      if (defaultCaseReducer) {
        builder.addDefaultCase(defaultCaseReducer)
      }
    })
  }

  const selectSelf = (state: State) => state
  // 用于缓存注入的 selectors
  const injectedSelectorCache = new Map<
    boolean,
    WeakMap<
      (rootState: any) => State | undefined,
      Record<string, (rootState: any) => any>
    >
  >()

  let _reducer: ReducerWithInitialState<State>
  // 获取当前的 reducer
  function reducer(state: State | undefined, action: UnknownAction) {
    if (!_reducer) _reducer = buildReducer()

    return _reducer(state, action)
  }
  // 获取初始状态
  function getInitialState() {
    if (!_reducer) _reducer = buildReducer()

    return _reducer.getInitialState()
  }
  // 构建 selectors
  function makeSelectorProps<CurrentReducerPath extends string = ReducerPath>(
    reducerPath: CurrentReducerPath,
    injected = false,
  ): Pick<
    Slice<State, CaseReducers, Name, CurrentReducerPath, Selectors>,
    'getSelectors' | 'selectors' | 'selectSlice' | 'reducerPath'
  > {
    function selectSlice(state: { [K in CurrentReducerPath]: State }) {
      ...
    }
    function getSelectors(
      selectState: (rootState: any) => State = selectSelf,
    ) {
      ...
    }
    return {
      reducerPath,
      getSelectors,
      get selectors() {
        return getSelectors(selectSlice)
      },
      selectSlice,
    }
  }
  // 返回 slice 对象
  const slice: Slice<State, CaseReducers, Name, ReducerPath, Selectors> = {
    name,
    reducer,
    actions: context.actionCreators as any,
    caseReducers: context.sliceCaseReducersByName as any,
    getInitialState,
    ...makeSelectorProps(reducerPath),
    // 注入功能,允许将 slice 注入到其他 slice 中,以便共享状态和 actions
    injectInto(injectable, { reducerPath: pathOpt, ...config } = {}) {
      const newReducerPath = pathOpt ?? reducerPath
      injectable.inject({ reducerPath: newReducerPath, reducer }, config)
      return {
        ...slice,
        ...makeSelectorProps(newReducerPath, true),
      } as any
    },
  }
  return slice
}

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值