为一个大项目迁移到 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。 createSlice
或 createReducer
创建的 RTK reducer 与您手写的 reducer 无异于“外面”。这意味着您可以随心所欲地使用它们,甚至可以将它们放入您的 mergeReducers
.
我们的建议:将您的 createStore
切换到 configureStore
。这已经添加了一些辅助中间件,您的整个商店将从中获益 - 不变性和可序列化检查等等。然后只需将您已经慢慢切换到 RTK 的减速器 - 就是您无论如何都需要触摸的减速器。随着时间的推移,您的项目将会迁移。
至于去RTK查询——那只是范式的改变。当然你也可以随着时间的推移这样做,但是 reducer+saga 和 hooks+RTKQ 之间没有太多相似之处 - 你必须自己决定如何处理它。
我有一个包含庞大代码库的 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。 createSlice
或 createReducer
创建的 RTK reducer 与您手写的 reducer 无异于“外面”。这意味着您可以随心所欲地使用它们,甚至可以将它们放入您的 mergeReducers
.
我们的建议:将您的 createStore
切换到 configureStore
。这已经添加了一些辅助中间件,您的整个商店将从中获益 - 不变性和可序列化检查等等。然后只需将您已经慢慢切换到 RTK 的减速器 - 就是您无论如何都需要触摸的减速器。随着时间的推移,您的项目将会迁移。
至于去RTK查询——那只是范式的改变。当然你也可以随着时间的推移这样做,但是 reducer+saga 和 hooks+RTKQ 之间没有太多相似之处 - 你必须自己决定如何处理它。