diff --git a/src/web/hooks/__tests__/useShallowEqualSelector.jsx b/src/web/hooks/__tests__/useShallowEqualSelector.jsx
new file mode 100644
index 0000000000..8f0f9fa382
--- /dev/null
+++ b/src/web/hooks/__tests__/useShallowEqualSelector.jsx
@@ -0,0 +1,96 @@
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+/* eslint-disable react/prop-types */
+
+import {useCallback} from 'react';
+import {useSelector, useDispatch} from 'react-redux';
+import {configureStore} from '@reduxjs/toolkit';
+
+import {describe, test, expect, testing} from '@gsa/testing';
+
+import {fireEvent, rendererWith, screen} from 'web/utils/testing';
+
+import useShallowEqualSelector from '../useShallowEqualSelector';
+
+const reducer = (state = {value: 0}, action) => {
+ switch (action.type) {
+ case 'increment':
+ return {...state, value: 1};
+ default:
+ return state;
+ }
+};
+
+const update = () => ({type: 'increment'});
+
+const TestComponent1 = ({renderCallback}) => {
+ const state = useSelector(state => state.counter);
+ const dispatch = useDispatch();
+ const updateCounter = useCallback(() => dispatch(update()), [dispatch]);
+ renderCallback();
+ return (
+
+
{state.value}
+
+
+ );
+};
+
+const TestComponent2 = ({renderCallback}) => {
+ const state = useShallowEqualSelector(state => state.counter);
+ renderCallback();
+ return (
+
+ );
+};
+
+describe('useShallowEqualSelector tests', () => {
+ test('should return the selected state', () => {
+ const renderCount = testing.fn();
+ const shallowRenderCount = testing.fn();
+ const store = configureStore({
+ reducer: {
+ counter: reducer,
+ },
+ middleware: () => [],
+ });
+
+ const {render} = rendererWith({store});
+
+ render(
+ <>
+
+
+ >,
+ );
+
+ const counter = screen.getByTestId('counter');
+ const shallowCounter = screen.getByTestId('shallowCounter');
+ expect(counter).toHaveTextContent('0');
+ expect(shallowCounter).toHaveTextContent('0');
+ expect(renderCount).toHaveBeenCalledTimes(1);
+ expect(shallowRenderCount).toHaveBeenCalledTimes(1);
+
+ const updateCounter = screen.getByTestId('update');
+ fireEvent.click(updateCounter);
+
+ expect(counter).toHaveTextContent('1');
+ expect(renderCount).toHaveBeenCalledTimes(2);
+ expect(shallowCounter).toHaveTextContent('1');
+ expect(shallowRenderCount).toHaveBeenCalledTimes(2);
+
+ fireEvent.click(updateCounter);
+
+ expect(counter).toHaveTextContent('1');
+ expect(renderCount).toHaveBeenCalledTimes(3);
+ expect(shallowCounter).toHaveTextContent('1');
+ expect(shallowRenderCount).toHaveBeenCalledTimes(2);
+ });
+});
diff --git a/src/web/hooks/useShallowEqualSelector.js b/src/web/hooks/useShallowEqualSelector.js
new file mode 100644
index 0000000000..5cf2ca5dbc
--- /dev/null
+++ b/src/web/hooks/useShallowEqualSelector.js
@@ -0,0 +1,20 @@
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import {useSelector, shallowEqual} from 'react-redux';
+
+/**
+ * A hook to use a redux selector with shallow equality check
+ *
+ * By default useSelector uses a strict equality check `===` to determine if the
+ * state has changed. This hook uses a shallow equality check to determine if the
+ * state has changed.
+ *
+ * @param {*} selector A redux selector
+ * @returns {*} The selected state
+ */
+const useShallowEqualSelector = selector => useSelector(selector, shallowEqual);
+
+export default useShallowEqualSelector;