feat(admin): implement redux-toolkit

This commit is contained in:
Josh 2023-10-16 11:05:24 +01:00
parent 4fcaf0f227
commit 57aef2ac2b
6 changed files with 120 additions and 78 deletions

View File

@ -11,8 +11,8 @@ import { BrowserRouter } from 'react-router-dom';
import Logo from './assets/images/logo-strapi-2022.svg';
import { LANGUAGE_LOCAL_STORAGE_KEY } from './components/LanguageProvider';
import Providers from './components/Providers';
import { customFields, Plugin } from './core/apis';
import configureStore from './core/store/configureStore';
import { customFields, Plugin, Reducers } from './core/apis';
import { configureStore } from './core/store/configureStore';
import { basename, createHook } from './core/utils';
import {
INJECT_COLUMN_IN_TABLE,
@ -26,7 +26,7 @@ import App from './pages/App';
import languageNativeNames from './translations/languageNativeNames';
class StrapiApp {
constructor({ adminConfig, appPlugins, library, middlewares, reducers }) {
constructor({ adminConfig, appPlugins, library, middlewares }) {
this.customConfigurations = adminConfig.config;
this.customBootstrapConfiguration = adminConfig.bootstrap;
this.configurations = {
@ -43,7 +43,7 @@ class StrapiApp {
this.library = library;
this.middlewares = middlewares;
this.plugins = {};
this.reducers = reducers;
this.reducers = Reducers({});
this.translations = {};
this.hooksDict = {};
this.admin = {

View File

@ -1,47 +0,0 @@
import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
const configureStore = (appMiddlewares, appReducers) => {
let composeEnhancers = compose;
const middlewares = [];
appMiddlewares.forEach((middleware) => {
middlewares.push(middleware());
});
// If Redux Dev Tools are installed, enable them
if (
process.env.NODE_ENV !== 'production' &&
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
) {
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({});
}
const store = createStore(
createReducer(appReducers, {}),
{},
composeEnhancers(applyMiddleware(...middlewares))
);
// Add a dictionary to keep track of the registered async reducers
store.asyncReducers = {};
// Create an inject reducer function
// This function adds the async reducer, and creates a new combined reducer
store.injectReducer = (key, asyncReducer) => {
store.asyncReducers[key] = asyncReducer;
store.replaceReducer(createReducer(appReducers, store.asyncReducers));
};
return store;
};
const createReducer = (appReducers, asyncReducers) => {
return combineReducers({
...appReducers,
...asyncReducers,
});
};
export default configureStore;

View File

@ -0,0 +1,114 @@
import {
configureStore,
StoreEnhancer,
Middleware,
Reducer,
combineReducers,
createSelector,
Selector,
} from '@reduxjs/toolkit';
import { useDispatch, useStore, TypedUseSelectorHook, useSelector } from 'react-redux';
// @ts-expect-error no types, yet.
import rbacProviderReducer from '../../components/RBACProvider/reducer';
// @ts-expect-error no types, yet.
import rbacManagerReducer from '../../content-manager/hooks/useSyncRbac/reducer';
// @ts-expect-error no types, yet.
import cmAppReducer from '../../content-manager/pages/App/reducer';
// @ts-expect-error no types, yet.
import editViewLayoutManagerReducer from '../../content-manager/pages/EditViewLayoutManager/reducer';
// @ts-expect-error no types, yet.
import listViewReducer from '../../content-manager/pages/ListView/reducer';
// @ts-expect-error no types, yet.
import editViewCrudReducer from '../../content-manager/sharedReducers/crudReducer/reducer';
// @ts-expect-error no types, yet.
import appReducer from '../../pages/App/reducer';
const createReducer = (
appReducers: Record<string, Reducer>,
asyncReducers: Record<string, Reducer>
) => {
return combineReducers({
...appReducers,
...asyncReducers,
});
};
/**
* @description Static reducers are ones we know, they live in the admin package.
*/
const staticReducers: Record<string, Reducer> = {
admin_app: appReducer,
rbacProvider: rbacProviderReducer,
'content-manager_app': cmAppReducer,
'content-manager_listView': listViewReducer,
'content-manager_rbacManager': rbacManagerReducer,
'content-manager_editViewLayoutManager': editViewLayoutManagerReducer,
'content-manager_editViewCrudReducer': editViewCrudReducer,
} as const;
const injectReducerStoreEnhancer: (appReducers: Record<string, Reducer>) => StoreEnhancer =
(appReducers) =>
(next) =>
(...args) => {
const store = next(...args);
const asyncReducers: Record<string, Reducer> = {};
return {
...store,
asyncReducers,
injectReducer: (key: string, asyncReducer: Reducer) => {
asyncReducers[key] = asyncReducer;
// @ts-expect-error we dynamically add reducers which makes the types uncomfortable.
store.replaceReducer(createReducer(appReducers, asyncReducers));
},
};
};
/**
* @description This is the main store configuration function, injected Reducers use our legacy app.addReducer API,
* which we're trying to phase out. App Middlewares could potentially be improved...?
*/
const configureStoreImpl = (
appMiddlewares: Array<() => Middleware>,
injectedReducers: Record<string, Reducer>
) => {
const coreReducers = { ...staticReducers, ...injectedReducers };
const store = configureStore({
reducer: createReducer(coreReducers, {}),
devTools: process.env.NODE_ENV !== 'production',
middleware: (getDefaultMiddleware) => [
...getDefaultMiddleware(),
...appMiddlewares.map((m) => m()),
],
enhancers: [injectReducerStoreEnhancer(coreReducers)],
});
return store;
};
type Store = ReturnType<typeof configureStoreImpl> & {
asyncReducers: Record<string, Reducer>;
injectReducer: (key: string, asyncReducer: Reducer) => void;
};
type RootState = ReturnType<Store['getState']>;
type AppDispatch = Store['dispatch'];
const useTypedDispatch: () => AppDispatch = useDispatch;
const useTypedStore = useStore as () => Store;
const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector;
const createTypedSelector = <TResult>(selector: Selector<RootState, TResult>) =>
createSelector((state: RootState) => state, selector);
export {
useTypedDispatch,
useTypedStore,
useTypedSelector,
configureStoreImpl as configureStore,
createTypedSelector,
};
export type { RootState };

View File

@ -2,10 +2,9 @@ import { getFetchClient } from '@strapi/helper-plugin';
import { createRoot } from 'react-dom/client';
import appCustomisations from './app';
import { Components, Fields, Middlewares, Reducers } from './core/apis';
import { Components, Fields, Middlewares } from './core/apis';
// eslint-disable-next-line import/extensions
import plugins from './plugins';
import appReducers from './reducers';
window.strapi = {
/**
@ -35,7 +34,6 @@ const library = {
fields: Fields(),
};
const middlewares = Middlewares();
const reducers = Reducers({ appReducers });
const MOUNT_NODE = document.getElementById('app');
@ -69,7 +67,6 @@ const run = async () => {
adminConfig: customConfig,
bootstrap: customConfig,
middlewares,
reducers,
});
await app.bootstrapAdmin();

View File

@ -1,23 +0,0 @@
import rbacProviderReducer from './components/RBACProvider/reducer';
import rbacManagerReducer from './content-manager/hooks/useSyncRbac/reducer';
import cmAppReducer from './content-manager/pages/App/reducer';
import editViewLayoutManagerReducer from './content-manager/pages/EditViewLayoutManager/reducer';
import listViewReducer from './content-manager/pages/ListView/reducer';
import editViewCrudReducer from './content-manager/sharedReducers/crudReducer/reducer';
import appReducer from './pages/App/reducer';
const contentManagerReducers = {
'content-manager_app': cmAppReducer,
'content-manager_listView': listViewReducer,
'content-manager_rbacManager': rbacManagerReducer,
'content-manager_editViewLayoutManager': editViewLayoutManagerReducer,
'content-manager_editViewCrudReducer': editViewCrudReducer,
};
const reducers = {
admin_app: appReducer,
rbacProvider: rbacProviderReducer,
...contentManagerReducers,
};
export default reducers;

View File

@ -44,6 +44,7 @@
"@casl/ability": "6.5.0",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.10",
"@radix-ui/react-toolbar": "1.0.4",
"@reduxjs/toolkit": "1.9.7",
"@strapi/data-transfer": "4.14.4",
"@strapi/design-system": "1.12.2",
"@strapi/helper-plugin": "4.14.4",