微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 Redux Toolkit 中使用多个切片时,如何监听全局动作并直接更新 rootState? 概念概述输入动作修改Reducer

如何解决在 Redux Toolkit 中使用多个切片时,如何监听全局动作并直接更新 rootState? 概念概述输入动作修改Reducer

我的 Redux 商店中有一个状态如下:

type RootState = {
 PAGE_A: StatePageA,PAGE_B: StatePageB,PAGE_C: StatePageC,// AND SO ON...
}

每个页面都是使用 createSlice 中的 @reduxjs/toolkit 创建的切片

我正在使用 Next.js 静态生成和 SSR,因此我需要使用来自 Next.js 服务器后续页面的新状态来补充我之前创建的存储。

无论我要路由到哪个页面,我都会得到预渲染的 pageState,它的类型应该是 StatePageA | StatePageB | StatePageC

当新状态出现时,我需要dispatch()一个HYdratE_PAGE操作,它应该替换当前页面状态。

这是我关于如何处理 HYdratE_PAGE 操作的第一个想法。

export const HYdratE_PAGE = createAction<{
  pageName: "PAGE_A" | "PAGE_B" | "PAGE_C",pageState: StatePageA | StatePageB | StatePageC
}>("HYdratE_PAGE");

然后我将不得不在每个切片中聆听它:

// EX: PAGE_A SLICE
// THIS WOULD HAVE TO BE DONE FOR EACH SLICE ON THE extraReducers PROPERTY

extraReducers: {
  [HYdratE_PAGE]: (state,action) => {
    if (action.payload.pageName === "PAGE_A")
      return action.payload.pageState
  }
}

有没有一种方法可以集中处理它,而不必在每个 slice reducer 中监听那个动作?

例如:rootReducer 类型的事情,我可以在其中监听 HYdratE_PAGE 操作并在一个地方处理它。

// THIS REDUCER WOULD HAVE ACCESS TO THE rootState
(state: RootState,action) => {
  const {pageName,pageState} = action.payload;
  state[pageName] = pageState;
};

这有可能吗?


额外

一个名为 next-redux-wrapper 的包,但我不打算使用它。我正在尝试构建我的自定义解决方案。

解决方法

概念概述

我不确定您的商店是否具有最佳设计,因为通常您希望按数据类型而不是在哪个页面上使用数据来组织数据。但我只想回答这里提出的问题。

reducer 只是一个函数,它接受一个状态和一个动作并返回下一个状态。当您使用 combineReducers(或 configureStore,在内部使用 combineReducers)组合切片缩减器的键控对象时,您会得到一个如下所示的函数:

(state: RootState | undefined,action: AnyAction) => RootState

我们希望围绕该函数创建一个包装器,并添加一个额外的步骤来执行 HYDRATE_PAGE 操作。


输入动作

export const HYDRATE_PAGE = createAction<{
  pageName: "PAGE_A" | "PAGE_B" | "PAGE_C",pageState: StatePageA | StatePageB | StatePageC
}>("HYDRATE_PAGE");

您的动作创建器中的打字稿类型并不像它们所能达到的那么好,因为我们希望强制 pageNamepageState 相互匹配。我们真正想要的类型是这样的:

type HydratePayload = {
    [K in keyof RootState]: {
        pageName: K;
        pageState: RootState[K];
    }
}[keyof RootState];
export const HYDRATE_PAGE = createAction<HydratePayload>("HYDRATE_PAGE");

计算结果为有效配对的并集:

type HydratePayload = {
    pageName: "PAGE_A";
    pageState: StatePageA;
} | {
    pageName: "PAGE_B";
    pageState: StatePageB;
} | {
    pageName: "PAGE_C";
    pageState: StatePageC;
}

修改Reducer

首先,我们将使用 rootReducer 创建普通的 combineReducers。然后我们创建 hydratedReducer 作为它的包装器。我们调用 rootReducer(state,action) 来获取下一个状态。大多数时候我们只是返回那个状态而不做任何事情。 但是,如果 action 与我们的 HYDRATE_PAGE 操作匹配,那么我们通过用我们的有效负载中的状态替换该切片的状态来修改状态。

const rootReducer = combineReducers({
  PAGE_A: pageAReducer,PAGE_B: pageBReducer,PAGE_C: pageCReducer
});

const hydratedReducer = (state: RootState | undefined,action: AnyAction): RootState => {
  // call the reducer normally
  const nextState = rootReducer(state,action);
  // modify the next state when the action is hydrate
  if (HYDRATE_PAGE.match(action)) {
    const { pageName,pageState } = action.payload;
    return {
      ...nextState,[pageName]: pageState
    };
  }
  // otherwise don't do anything
  return nextState;
}

const store = configureStore({
  reducer: hydratedReducer,});

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。