为一个大项目迁移到 redux-toolkit

Posted

技术标签:

【中文标题】为一个大项目迁移到 redux-toolkit【英文标题】:Migrate to redux-toolkit for a big project 【发布时间】:2022-01-03 09:33:57 【问题描述】:

我有一个包含庞大代码库的 react 项目。我正在使用 redux、sagas 和异步减速器。 我有一个 redux-module 结构。有很多小的减速器,我将它们组合成几个大的异步减速器。 sagas 的情况也一样。

是否可以逐步迁移到redux-toolkit和rtk-query?我不能一次重写所有模块。也许有人有做同样迁移的经验?或者我应该保留我的样板代码库?)

这是异步 redux 模块之一的代码

const reducer: (state: UserState, action: UserAction) => UserState = mergeReducers(
  userReducer,
  settingsReducer,
  signUpReducer
);

mergeReducers 函数

const mergeReducers =
  (...reducers) =>
  (state, action) =>
    reducers.reduce((acc, reducer) => ( ...acc, ...reducer(acc, action) ), state);

所有这些减速器都是标准减速器,例如

const signUpReducer = (state: UserState = userInitialState, action: SignUpAction): UserState => 
  switch (action.type) 
    case SIGN_UP_CONTINUE_REQUESTED:
    case SIGN_UP_INITIATE_REQUESTED:
      return  ...state, pending: true, error: null ;
    case SIGN_UP_INITIATE_SUCCEEDED:
      return  ...state, pending: false ;
    case SIGN_UP_CONTINUE_SUCCEEDED:
      return 
        ...state,
        profile: action.payload.profile,
        token: action.payload.token,
        error: null,
      ;
    case SIGN_UP_INITIATE_REJECTED:
    case SIGN_UP_CONTINUE_REJECTED:
      return  ...state, pending: false, error: action.payload ;

    default: 
      /* ::(action.type: empty) */
      return  ...state ;
    
  
;

这里的模块实现

function startSaga(key, saga) 
  const runnableSaga = function* main() 
    const sagaTask = yield fork(saga);
    const  payload  = yield take(STOP_SAGA);

    if (payload === key) 
      yield cancel(sagaTask);
    
  ;

  sagaMiddleware.run(runnableSaga);


function stopSaga(key) 
  store.dispatch(
    payload: key,
    type: STOP_SAGA,
  );


export const useReduxModule = (key, reducer, saga, initialAction) => 
  useEffect(() => 
    if (!store.asyncReducers[key]) 
      store.injectReducer(key, reducer);
      startSaga(key, saga);
      if (initialAction) initialAction();
    

    return () => 
      stopSaga(key);
      store.removeReducer(key);
    ;
  , []);
;

在反应中使用。 我需要在模块根组件中初始化redux模块

import  loadSomeDateRequested, reducer, saga  from './store';

const SomeComponent = ( loadData ) => 
  useReduxModule(SOME_MODULE, reducer, saga, loadData);

  return (
    // some jsx
  );
;

export default connect(null, 
  loadData: loadSomeDateRequested,
)(SomeComponent);

SomeComponent.propTypes = 
  loadData: func.isRequired,
;

存储配置

function createReducer(asyncReducers) 
  return combineReducers(
    ...staticReducers,
    ...asyncReducers,
  );


export const sagaMiddleware = createSagaMiddleware();

const bindMiddleware = (middlewares) =>
  (process.env.NODE_ENV !== 'production' && composeWithDevTools(applyMiddleware(...middlewares))) ||
  applyMiddleware(...middlewares);

export default function configureStore() 
  const store = createStore(createReducer(), bindMiddleware([sagaMiddleware]));
  store.asyncReducers = ;
  store.injectReducer = (key, asyncReducer) => 
    store.asyncReducers[key] = asyncReducer;
    store.replaceReducer(createReducer(store.asyncReducers));
  ;
  store.removeReducer = (key) => 
    delete store.asyncReducers[key];
    delete store.getState()[key];
  ;
  return store;


export const store = configureStore();

静态减速器是

export default 
  [MODULE_PDF_MODAL]: pdfModalReducer,
;

我花了很多时间研究和阅读文档和示例。但是我没有找到真实项目的迁移示例,只有如何迁移最简单的 redux 存储的示例。也许有人知道如何添加一个 redux 工具包并保持现有商店的工作。因为目前,我只知道一种解决方案。而这个解决方案是一次性重写所有的redux store。 正如我在上面写的那样,我有一些异步模块,它们彼此独立。按模块迁移是现实的,但我需要在重写之前保留所有其他的工作。

非常感谢您的所有回答。希望有人可以帮助我)

解决方案

import  configureStore  from '@reduxjs/toolkit';
import createSagaMiddleware from 'redux-saga';
import staticReducers from '@sharedStore/staticReducers';
import  combineReducers  from 'redux';

function createReducer(asyncReducers) 
  return combineReducers(
    ...staticReducers,
    ...asyncReducers,
  );


export const sagaMiddleware = createSagaMiddleware();

export default function configStore() 
  const store = configureStore(
    reducer: createReducer(),
    middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(sagaMiddleware),
  );
  store.asyncReducers = ;
  store.injectReducer = (key, asyncReducer) => 
    store.asyncReducers[key] = asyncReducer;
    store.replaceReducer(createReducer(store.asyncReducers));
  ;
  store.removeReducer = (key) => 
    delete store.asyncReducers[key];
    delete store.getState()[key];
  ;
  return store;


export const store = configStore();

您可以使用 redux-toolkit 并保持 redux 稳定运行。 对我来说,它是为了支持 redux 包中的 combineReducers 函数。

【问题讨论】:

【参考方案1】:

Redux Toolkit 就是 Redux。由createSlicecreateReducer 创建的RTK reducer 与您亲手写的reducer 与“外部”没有什么不同。这意味着您可以对它们做任何您想做的事情,甚至可以将它们放入您的mergeReducers

我们的建议:将您的 createStore 切换到 configureStore。这已经添加了一些辅助中间件,您的整个商店都将从中受益——不变性和可序列化检查等等。然后只需将您慢慢使用的减速器切换到 RTK - 只是您需要触摸的那些。随着时间的推移,您的项目将会迁移。

至于去 RTK Query - 那只是范式的改变。当然,随着时间的推移,你也可以这样做,但 reducer+saga 和 hooks+RTKQ 之间并没有太多相似之处——你必须自己决定如何处理。

【讨论】:

当然。我没有尝试在不删除用于 combineReducers 的 redux 的情况下安装工具包。

以上是关于为一个大项目迁移到 redux-toolkit的主要内容,如果未能解决你的问题,请参考以下文章

将项目添加到 redux-toolkit 中的嵌套数组

Python 如何导入 Github 大项目的一个模块? [复制]

gitlab 搭建 备份 升级 迁移恢复

redux-toolkit -> 检测到不可序列化的值

C ++ / QT中更大项目的结构[关闭]

关于大项目管理中的思考