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

Migrate to redux-toolkit for a big project

我有一个包含庞大代码库的 React 项目。我正在使用 redux、sagas 和异步 reducer。 我有一个 redux-module 结构。有很多小的减速器,我把它们组合成几个大的异步减速器。与传奇相同的情况。

是否可以逐步迁移到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 函数。

Redux Toolkit 就是 Redux。 createSlicecreateReducer 创建的 RTK reducer 与您手写的 reducer 无异于“外面”。这意味着您可以随心所欲地使用它们,甚至可以将它们放入您的 mergeReducers.

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

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