mirror of
https://github.com/strapi/strapi.git
synced 2025-11-01 18:33:55 +00:00
feat(admin): implement redux-toolkit
This commit is contained in:
parent
4fcaf0f227
commit
57aef2ac2b
@ -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 = {
|
||||
|
||||
@ -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;
|
||||
114
packages/core/admin/admin/src/core/store/configureStore.ts
Normal file
114
packages/core/admin/admin/src/core/store/configureStore.ts
Normal 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 };
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
@ -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",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user