Redux: Add Redux Toolkit
This commit adds Redux Toolkit as a dependency. Adding Redux Toolkit is beneficial because it will allow us to use the current best-practice Redux development patterns and tools as well as reduce the number of dependencies. Redux Toolkit is backwards compatible with existing Redux code, and therefore works with all of the current actions and reducers. The store is now created using Redux Toolkit's configureStore(). Previously, a custom store creator developed by Red Hat Insights was used. However, this is actually not required for Insights apps, and creating the store using configureStore() is necessary to take advantage of Redux Toolkit. For instance, the store can now be inspected using the Redux devtools in the browser. This commit removes the redux-logger middleware. It is no longer necessary, as the Redux development tools can now be used to easily inspect and reason about the redux store and its state. The Thunk middleware dependency has also been removed, as Thunks are already included in Redux Toolkit. The redux-promise-middleware dependency has been left in place for now, but its functionality is also available in Redux Toolkit and it may be considered for removal in the future. Using Redux Toolkit will also allow us to move to the `Slice` pattern when defining actions/reducers in the future if we wish. This will make writing, reasoning about, and debugging the code related to the Redux store much easier.
This commit is contained in:
parent
1d6a92a7f9
commit
a977f4b72b
6 changed files with 85 additions and 81 deletions
89
package-lock.json
generated
89
package-lock.json
generated
|
|
@ -16,15 +16,14 @@
|
|||
"@redhat-cloud-services/frontend-components": "3.9.2",
|
||||
"@redhat-cloud-services/frontend-components-notifications": "3.2.7",
|
||||
"@redhat-cloud-services/frontend-components-utilities": "3.2.16",
|
||||
"@reduxjs/toolkit": "^1.8.5",
|
||||
"classnames": "2.3.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-redux": "8.0.2",
|
||||
"react-router-dom": "6.3.0",
|
||||
"redux": "4.1.2",
|
||||
"redux-logger": "3.0.6",
|
||||
"redux-promise-middleware": "6.1.2",
|
||||
"redux-thunk": "2.4.1"
|
||||
"redux-promise-middleware": "6.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.18.6",
|
||||
|
|
@ -3229,6 +3228,29 @@
|
|||
"resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-0.0.1.tgz",
|
||||
"integrity": "sha512-UgHgLf8LkqaD9PXjiVyYycMRlbvmVdJyoJfersCUwnzDMgp+DEoYlLQAa9kn/s4c1JvSt5MM+hEN+DRvwtCQGA=="
|
||||
},
|
||||
"node_modules/@reduxjs/toolkit": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.5.tgz",
|
||||
"integrity": "sha512-f4D5EXO7A7Xq35T0zRbWq5kJQyXzzscnHKmjnu2+37B3rwHU6mX9PYlbfXdnxcY6P/7zfmjhgan0Z+yuOfeBmA==",
|
||||
"dependencies": {
|
||||
"immer": "^9.0.7",
|
||||
"redux": "^4.1.2",
|
||||
"redux-thunk": "^2.4.1",
|
||||
"reselect": "^4.1.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.9.0 || ^17.0.0 || ^18",
|
||||
"react-redux": "^7.2.1 || ^8.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-redux": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@scalprum/core": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@scalprum/core/-/core-0.1.2.tgz",
|
||||
|
|
@ -6422,11 +6444,6 @@
|
|||
"integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/deep-diff": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz",
|
||||
"integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ="
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
|
|
@ -8761,6 +8778,15 @@
|
|||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immer": {
|
||||
"version": "9.0.15",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz",
|
||||
"integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
|
||||
|
|
@ -13822,14 +13848,6 @@
|
|||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/redux-logger": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz",
|
||||
"integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=",
|
||||
"dependencies": {
|
||||
"deep-diff": "^0.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/redux-mock-store": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.4.tgz",
|
||||
|
|
@ -14014,6 +14032,11 @@
|
|||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/reselect": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz",
|
||||
"integrity": "sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ=="
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
|
||||
|
|
@ -19140,6 +19163,17 @@
|
|||
"resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-0.0.1.tgz",
|
||||
"integrity": "sha512-UgHgLf8LkqaD9PXjiVyYycMRlbvmVdJyoJfersCUwnzDMgp+DEoYlLQAa9kn/s4c1JvSt5MM+hEN+DRvwtCQGA=="
|
||||
},
|
||||
"@reduxjs/toolkit": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.5.tgz",
|
||||
"integrity": "sha512-f4D5EXO7A7Xq35T0zRbWq5kJQyXzzscnHKmjnu2+37B3rwHU6mX9PYlbfXdnxcY6P/7zfmjhgan0Z+yuOfeBmA==",
|
||||
"requires": {
|
||||
"immer": "^9.0.7",
|
||||
"redux": "^4.1.2",
|
||||
"redux-thunk": "^2.4.1",
|
||||
"reselect": "^4.1.5"
|
||||
}
|
||||
},
|
||||
"@scalprum/core": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@scalprum/core/-/core-0.1.2.tgz",
|
||||
|
|
@ -21704,11 +21738,6 @@
|
|||
"integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
|
||||
"dev": true
|
||||
},
|
||||
"deep-diff": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz",
|
||||
"integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ="
|
||||
},
|
||||
"deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
|
|
@ -23453,6 +23482,11 @@
|
|||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
||||
"dev": true
|
||||
},
|
||||
"immer": {
|
||||
"version": "9.0.15",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz",
|
||||
"integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ=="
|
||||
},
|
||||
"immutable": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
|
||||
|
|
@ -27180,14 +27214,6 @@
|
|||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"redux-logger": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz",
|
||||
"integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=",
|
||||
"requires": {
|
||||
"deep-diff": "^0.3.5"
|
||||
}
|
||||
},
|
||||
"redux-mock-store": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.4.tgz",
|
||||
|
|
@ -27334,6 +27360,11 @@
|
|||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
||||
"dev": true
|
||||
},
|
||||
"reselect": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz",
|
||||
"integrity": "sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
|
||||
|
|
|
|||
|
|
@ -15,15 +15,14 @@
|
|||
"@redhat-cloud-services/frontend-components": "3.9.2",
|
||||
"@redhat-cloud-services/frontend-components-notifications": "3.2.7",
|
||||
"@redhat-cloud-services/frontend-components-utilities": "3.2.16",
|
||||
"@reduxjs/toolkit": "^1.8.5",
|
||||
"classnames": "2.3.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-redux": "8.0.2",
|
||||
"react-router-dom": "6.3.0",
|
||||
"redux": "4.1.2",
|
||||
"redux-logger": "3.0.6",
|
||||
"redux-promise-middleware": "6.1.2",
|
||||
"redux-thunk": "2.4.1"
|
||||
"redux-promise-middleware": "6.1.2"
|
||||
},
|
||||
"jest": {
|
||||
"coverageDirectory": "./coverage/",
|
||||
|
|
|
|||
|
|
@ -1,18 +1,12 @@
|
|||
import React from 'react';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { init } from './store';
|
||||
import App from './App';
|
||||
import { getBaseName } from '@redhat-cloud-services/frontend-components-utilities/helpers';
|
||||
import logger from 'redux-logger';
|
||||
import { store } from './store';
|
||||
|
||||
const ImageBuilder = () => (
|
||||
<Provider
|
||||
store={init(
|
||||
{},
|
||||
...[process.env.NODE_ENV !== 'production' ? logger : undefined]
|
||||
).getStore()}
|
||||
>
|
||||
<Provider store={store}>
|
||||
<Router basename={getBaseName(window.location.pathname)}>
|
||||
<App />
|
||||
</Router>
|
||||
|
|
|
|||
|
|
@ -1,38 +1,14 @@
|
|||
import { ReducerRegistry } from '@redhat-cloud-services/frontend-components-utilities/ReducerRegistry';
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import composes from './reducers/composes';
|
||||
import promiseMiddleware from 'redux-promise-middleware';
|
||||
import thunk from 'redux-thunk';
|
||||
import { notificationsReducer } from '@redhat-cloud-services/frontend-components-notifications/redux';
|
||||
|
||||
import composes from './reducers/composes';
|
||||
export const reducer = {
|
||||
composes: composes,
|
||||
notifications: notificationsReducer,
|
||||
};
|
||||
|
||||
let registry;
|
||||
export const middleware = (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware().concat(promiseMiddleware);
|
||||
|
||||
export function init(store = {}, ...middleware) {
|
||||
if (!registry) {
|
||||
registry = new ReducerRegistry(store, [
|
||||
promiseMiddleware,
|
||||
thunk,
|
||||
...middleware.filter((item) => typeof item !== 'undefined'),
|
||||
]);
|
||||
|
||||
registry.register({
|
||||
composes,
|
||||
notifications: notificationsReducer,
|
||||
});
|
||||
}
|
||||
|
||||
return registry;
|
||||
}
|
||||
|
||||
export function getStore() {
|
||||
return registry.getStore();
|
||||
}
|
||||
|
||||
export function register(...args) {
|
||||
return registry.register(...args);
|
||||
}
|
||||
|
||||
/* added for testing purposes only */
|
||||
export function clearStore() {
|
||||
registry = undefined;
|
||||
}
|
||||
export const store = configureStore({ reducer, middleware });
|
||||
|
|
|
|||
|
|
@ -1343,7 +1343,7 @@ describe('Click through all steps', () => {
|
|||
const setUp = async () => {
|
||||
const view = renderWithReduxRouter(<CreateImageWizard />);
|
||||
history = view.history;
|
||||
store = view.reduxStore;
|
||||
store = view.store;
|
||||
};
|
||||
|
||||
test('with valid values', async () => {
|
||||
|
|
@ -1767,7 +1767,7 @@ describe('Click through all steps', () => {
|
|||
|
||||
// returns back to the landing page
|
||||
await waitFor(() => expect(history.location.pathname).toBe('/'));
|
||||
expect(store.getStore().getState().composes.allIds).toEqual(ids);
|
||||
expect(store.getState().composes.allIds).toEqual(ids);
|
||||
// set test timeout of 10 seconds
|
||||
}, 10000);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,23 +1,27 @@
|
|||
import React from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { Provider } from 'react-redux';
|
||||
import { render } from '@testing-library/react';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { init, clearStore } from '../store';
|
||||
import { reducer, middleware } from '../store';
|
||||
|
||||
export const renderWithReduxRouter = (component, store = {}, route = '/') => {
|
||||
export const renderWithReduxRouter = (
|
||||
component,
|
||||
preloadedState = {},
|
||||
route = '/'
|
||||
) => {
|
||||
const history = createMemoryHistory({ initialEntries: [route] });
|
||||
clearStore();
|
||||
let reduxStore = init(store);
|
||||
const store = configureStore({ reducer, middleware, preloadedState });
|
||||
return {
|
||||
...render(
|
||||
<Provider store={reduxStore.getStore()}>
|
||||
<Provider store={store}>
|
||||
<Router location={history.location} navigator={history}>
|
||||
{component}
|
||||
</Router>
|
||||
</Provider>
|
||||
),
|
||||
history,
|
||||
reduxStore,
|
||||
store,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue