From a4c7a4cf22d1a87d838f454cf25c558e1a5b933f Mon Sep 17 00:00:00 2001 From: Siarhei Karol <135722306+SKarolFolio@users.noreply.github.com> Date: Sat, 14 Dec 2024 12:24:47 +0500 Subject: [PATCH] UILD-405: change state management library (#52) --- package-lock.json | 21 ---- package.json | 4 +- src/App.tsx | 12 +- src/common/constants/bundle.constants.ts | 1 + src/common/hooks/useCommonStatus.ts | 7 +- src/common/hooks/useComplexLookup.ts | 20 ++-- .../hooks/useComplexLookupSearchResults.ts | 6 +- src/common/hooks/useConfig.hook.ts | 27 ++--- src/common/hooks/useContainerEvents.ts | 14 +-- src/common/hooks/useFetchSearchData.ts | 24 +--- src/common/hooks/useLoadSearchResults.ts | 11 +- src/common/hooks/useMarcData.ts | 18 +-- src/common/hooks/useNavigateToEditPage.ts | 5 +- src/common/hooks/usePagination.ts | 5 +- .../hooks/useProcessedRecordAndSchema.hook.ts | 14 +-- src/common/hooks/useProfileSchema.ts | 12 +- src/common/hooks/useRecordControls.ts | 67 ++++------- src/common/hooks/useRecordGeneration.ts | 9 +- src/common/hooks/useRecordStatus.ts | 5 +- src/common/hooks/useResetRecordStatus.ts | 7 +- src/common/hooks/useSearch.ts | 38 ++++--- .../hooks/useSearchFilterLookupOptions.ts | 6 +- src/common/hooks/useSearchFilters.ts | 11 +- src/common/hooks/useSearchFiltersData.ts | 21 +--- src/common/hooks/useSearchNavigationState.ts | 5 +- .../AdvancedSearchModal.tsx | 7 +- src/components/Announcement/index.ts | 1 + src/components/CommonStatus/CommonStatus.tsx | 7 +- .../MarcPreviewComplexLookup.tsx | 8 +- .../ModalComplexLookup.scss | 4 + .../ComplexLookupField/ModalComplexLookup.tsx | 20 ++-- src/components/DeleteRecord/DeleteRecord.tsx | 7 +- .../DuplicateGroupContainer.tsx | 9 +- .../EditControlPane/EditControlPane.tsx | 12 +- src/components/EditPreview/EditPreview.tsx | 9 +- src/components/EditSection/EditSection.tsx | 37 +++--- .../EditSection/renderDrawComponent.tsx | 4 +- .../FieldWithMetadataAndControls.tsx | 7 +- src/components/Fields/Fields.tsx | 9 +- src/components/Footer/Footer.tsx | 7 +- src/components/FullDisplay/FullDisplay.tsx | 5 +- src/components/FullDisplay/PreviewContent.tsx | 5 +- src/components/ItemSearch/ItemSearch.tsx | 5 +- .../ModalDuplicateImportedResource.tsx | 7 +- src/components/Nav/Nav.tsx | 7 +- src/components/Preview/Fields.tsx | 10 +- src/components/Preview/Preview.tsx | 8 +- .../PreviewExternalResourcePane.tsx | 7 +- src/components/Prompt/Prompt.tsx | 6 +- src/components/SaveRecord/SaveRecord.tsx | 9 +- .../SearchControlPane/SearchControlPane.tsx | 5 +- .../SearchControls/SearchControls.tsx | 29 ++--- .../SearchFilters/SearchFilters.tsx | 4 +- .../SearchResultEntry/SearchResultEntry.tsx | 13 +-- .../SearchResultList/SearchResultList.tsx | 5 +- .../SimpleLookupField/SimpleLookupField.tsx | 10 +- .../ViewMarcControlPane.tsx | 9 +- .../ViewMarcModal/ViewMarcModal.tsx | 5 +- src/providers/AsyncIntlProvider.tsx | 5 +- src/state/config.ts | 73 ------------ src/state/data.ts | 22 ---- src/state/index.ts | 17 --- src/state/inputs.ts | 28 ----- src/state/loadingState.ts | 10 -- src/state/search.ts | 82 -------------- src/state/status.ts | 28 ----- src/state/ui.ts | 46 -------- src/store/index.ts | 12 ++ src/store/selectors.ts | 19 ++++ src/store/stores/config.ts | 28 +++++ src/store/stores/inputs.ts | 35 ++++++ src/store/stores/loadingState.ts | 14 +++ src/store/stores/marcPreview.ts | 25 ++++ src/store/stores/profile.ts | 34 ++++++ src/store/stores/search.ts | 66 +++++++++++ src/store/stores/status.ts | 29 +++++ src/store/stores/ui.ts | 40 +++++++ src/store/utils/createStoreFactory.ts | 40 +++++++ src/store/utils/selectors.ts | 20 ++++ src/store/utils/slice.ts | 85 ++++++++++++++ src/store/utils/storeCreator.ts | 12 ++ .../common/constants/bundle.constants.mock.ts | 1 + src/test/__mocks__/setupMocks.ts | 1 + src/test/__mocks__/store/index.ts | 31 +++++ src/test/__mocks__/zustand.ts | 48 ++++++++ .../useComplesLookupSearchResults.test.ts | 12 +- .../common/hooks/useComplexLookup.test.ts | 26 ++--- .../common/hooks/useConfig.hook.test.ts | 76 +++++++------ .../common/hooks/useContainerEvents.test.ts | 20 +++- .../common/hooks/useLoadSearchResults.test.ts | 71 ++++++------ .../common/hooks/useLookupCache.test.ts | 5 +- .../hooks/useNavigateToEditPage.test.ts | 29 +++-- .../common/hooks/usePagination.test.ts | 90 ++++++++++----- .../hooks/useProcessedRecordAndSchema.test.ts | 22 ++-- .../common/hooks/useProfileSchema.test.ts | 21 +--- .../common/hooks/useRecordGeneration.test.ts | 26 +++-- .../common/hooks/useRecordStatus.test.ts | 11 +- .../__tests__/common/hooks/useSearch.test.ts | 107 +++++++++--------- .../useSearchFilterLookupOptions.test.ts | 13 ++- .../common/hooks/useSearchFilters.test.tsx | 35 +++--- .../common/hooks/useSearchFiltersData.test.ts | 37 +++--- .../hooks/useSearchNavigationState.test.ts | 26 ++--- .../components/AdvancedSearchModal.test.tsx | 37 +++--- .../components/CommonStatus.test.tsx | 20 ++-- .../ComplexLookupField.test.tsx | 28 +++-- .../components/DeleteRecord.test.tsx | 21 ++-- .../DuplicateGroupContainer.test.tsx | 13 +-- .../components/EditControlPane.test.tsx | 37 +++--- .../__tests__/components/EditPreview.test.tsx | 36 +++--- .../__tests__/components/EditSection.test.tsx | 42 ++++--- .../__tests__/components/FullDisplay.test.tsx | 37 +++--- .../components/InstancesList.test.tsx | 3 - .../__tests__/components/ItemSearch.test.tsx | 32 +++--- .../MarcPreviewComplexLookup.test.tsx | 27 ++--- src/test/__tests__/components/Nav.test.tsx | 7 +- .../__tests__/components/Preview.test.tsx | 42 ++++--- .../PreviewActionsDropdown.test.tsx | 6 +- .../PreviewExternalResourceControls.test.tsx | 23 ++-- .../PreviewExternalResourcePane.test.tsx | 21 ++-- src/test/__tests__/components/Prompt.test.tsx | 30 ++--- .../__tests__/components/SaveRecord.test.tsx | 25 ++-- .../components/SearchControls.test.tsx | 29 +++-- .../components/SearchFilters.test.tsx | 9 +- .../components/SearchResultEntry.test.tsx | 20 ++-- .../components/SearchResultList.test.tsx | 25 ++-- .../components/TitledPreview.test.tsx | 2 - .../components/ViewMarcControlPane.test.tsx | 7 +- .../components/ViewMarcModal.test.tsx | 17 +-- src/test/__tests__/store/utils/slice.test.ts | 99 ++++++++++++++++ src/test/__tests__/views/Edit.test.tsx | 28 +++-- .../views/ExternalResourcePreview.test.tsx | 25 ++-- src/test/__tests__/views/Root.test.tsx | 26 +++-- src/test/__tests__/views/Search.test.tsx | 9 +- src/types/store.d.ts | 3 + src/views/Edit/Edit.tsx | 21 ++-- .../ExternalResourcePreview.tsx | 5 +- src/views/Root/Root.tsx | 5 +- tsconfig.aliases.json | 1 - vite.config.ts | 1 - 139 files changed, 1637 insertions(+), 1327 deletions(-) create mode 100644 src/common/constants/bundle.constants.ts create mode 100644 src/components/Announcement/index.ts delete mode 100644 src/state/config.ts delete mode 100644 src/state/data.ts delete mode 100644 src/state/index.ts delete mode 100644 src/state/inputs.ts delete mode 100644 src/state/loadingState.ts delete mode 100644 src/state/search.ts delete mode 100644 src/state/status.ts delete mode 100644 src/state/ui.ts create mode 100644 src/store/index.ts create mode 100644 src/store/selectors.ts create mode 100644 src/store/stores/config.ts create mode 100644 src/store/stores/inputs.ts create mode 100644 src/store/stores/loadingState.ts create mode 100644 src/store/stores/marcPreview.ts create mode 100644 src/store/stores/profile.ts create mode 100644 src/store/stores/search.ts create mode 100644 src/store/stores/status.ts create mode 100644 src/store/stores/ui.ts create mode 100644 src/store/utils/createStoreFactory.ts create mode 100644 src/store/utils/selectors.ts create mode 100644 src/store/utils/slice.ts create mode 100644 src/store/utils/storeCreator.ts create mode 100644 src/test/__mocks__/common/constants/bundle.constants.mock.ts create mode 100644 src/test/__mocks__/store/index.ts create mode 100644 src/test/__mocks__/zustand.ts create mode 100644 src/test/__tests__/store/utils/slice.test.ts create mode 100644 src/types/store.d.ts diff --git a/package-lock.json b/package-lock.json index 08734005..ebb8aebf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "react-intl": "^6.4.4", "react-router-dom": "^6.20.0", "react-select": "^5.8.0", - "recoil": "^0.7.7", "uuid": "^9.0.0" }, "devDependencies": { @@ -7639,26 +7638,6 @@ "node": ">=8.10.0" } }, - "node_modules/recoil": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz", - "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==", - "license": "MIT", - "dependencies": { - "hamt_plus": "1.0.2" - }, - "peerDependencies": { - "react": ">=16.13.1" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", diff --git a/package.json b/package.json index 18da9727..da255e15 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,8 @@ "react-intl": "^6.4.4", "react-router-dom": "^6.20.0", "react-select": "^5.8.0", - "recoil": "^0.7.7", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "zustand": "^5.0.1" }, "devDependencies": { "@testing-library/dom": "^10.4.0", diff --git a/src/App.tsx b/src/App.tsx index 6e6b27d1..679e9b76 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,5 @@ import { FC, Suspense, useEffect, useRef } from 'react'; import { Navigate, RouteObject, RouterProvider, createBrowserRouter } from 'react-router-dom'; -import { RecoilRoot, useSetRecoilState } from 'recoil'; import { ErrorBoundary } from '@components/ErrorBoundary'; import { Loading } from '@components/Loading'; import { ROUTES } from '@common/constants/routes.constants'; @@ -8,10 +7,10 @@ import { DEFAULT_LOCALE } from '@common/constants/i18n.constants'; import { OKAPI_CONFIG } from '@common/constants/api.constants'; import { localStorageService } from '@common/services/storage'; import { Root, Search, EditWrapper, ExternalResourcePreview } from '@views'; -import state from '@state'; import en from '../translations/ui-linked-data/en.json'; import { AsyncIntlProvider, ServicesProvider } from './providers'; import './App.scss'; +import { useConfigState } from './store'; type IContainer = { routePrefix?: string; @@ -54,8 +53,7 @@ export const routes: RouteObject[] = [ const createRouter = (basename: string) => createBrowserRouter(routes, { basename }); const Container: FC = ({ routePrefix = '', config }) => { - const setCustomEvents = useSetRecoilState(state.config.customEvents); - const setHasNavigationOrigin = useSetRecoilState(state.config.hasNavigationOrigin); + const { setCustomEvents, setHasNavigationOrigin } = useConfigState(); const cachedMessages = useRef({ [DEFAULT_LOCALE]: en }); useEffect(() => { @@ -82,9 +80,5 @@ export const App: FC = ({ routePrefix = '', config }) => { config && localStorageService.serialize(OKAPI_CONFIG, config); }, [config]); - return ( - - - - ); + return ; }; diff --git a/src/common/constants/bundle.constants.ts b/src/common/constants/bundle.constants.ts new file mode 100644 index 00000000..53d96c17 --- /dev/null +++ b/src/common/constants/bundle.constants.ts @@ -0,0 +1 @@ +export const IS_PROD_MODE = import.meta.env.PROD; \ No newline at end of file diff --git a/src/common/hooks/useCommonStatus.ts b/src/common/hooks/useCommonStatus.ts index f3f81121..c67cd44c 100644 --- a/src/common/hooks/useCommonStatus.ts +++ b/src/common/hooks/useCommonStatus.ts @@ -1,13 +1,12 @@ -import { useSetRecoilState } from 'recoil'; import { UserNotificationFactory } from '@common/services/userNotification'; -import state from '@state'; +import { useStatusState } from '@src/store'; export const useCommonStatus = () => { - const setCommonStatus = useSetRecoilState(state.status.commonMessages); + const { addStatusMessagesItem } = useStatusState(); return { set: (l10nId: string, type: StatusType) => { - setCommonStatus(currentStatus => [...currentStatus, UserNotificationFactory.createMessage(type, l10nId)]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(type, l10nId)); }, }; }; diff --git a/src/common/hooks/useComplexLookup.ts b/src/common/hooks/useComplexLookup.ts index 225a7328..2def26c4 100644 --- a/src/common/hooks/useComplexLookup.ts +++ b/src/common/hooks/useComplexLookup.ts @@ -1,5 +1,4 @@ import { ChangeEvent, useCallback, useState } from 'react'; -import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'; import { generateEmptyValueUuid, getLinkedField, @@ -8,10 +7,10 @@ import { } from '@common/helpers/complexLookup.helper'; import { __MOCK_URI_CHANGE_WHEN_IMPLEMENTING } from '@common/constants/complexLookup.constants'; import { AdvancedFieldType } from '@common/constants/uiControls.constants'; -import state from '@state'; import { useModalControls } from './useModalControls'; import { useMarcData } from './useMarcData'; import { useServicesContext } from './useServicesContext'; +import { useInputsState, useMarcPreviewState, useProfileState, useUIState } from '@src/store'; export const useComplexLookup = ({ entry, @@ -26,14 +25,17 @@ export const useComplexLookup = ({ }) => { const { selectedEntriesService } = useServicesContext() as Required; const [localValue, setLocalValue] = useState(value || []); - const schema = useRecoilValue(state.config.schema); - const marcPreviewMetadata = useRecoilValue(state.data.marcPreviewMetadata); - const [selectedEntries, setSelectedEntries] = useRecoilState(state.config.selectedEntries); - const resetMarcPreviewData = useResetRecoilState(state.data.marcPreviewData); - const resetMarcPreviewMetadata = useResetRecoilState(state.data.marcPreviewMetadata); - const resetIsMarcPreviewOpen = useResetRecoilState(state.ui.isMarcPreviewOpen); + const { schema } = useProfileState(); + const { selectedEntries, setSelectedEntries } = useInputsState(); + const { + setComplexValue, + resetComplexValue: resetMarcPreviewData, + metadata: marcPreviewMetadata, + resetMetadata: resetMarcPreviewMetadata, + } = useMarcPreviewState(); + const { resetIsMarcPreviewOpen } = useUIState(); const { isModalOpen, setIsModalOpen, openModal } = useModalControls(); - const { fetchMarcData } = useMarcData(state.data.marcPreviewData); + const { fetchMarcData } = useMarcData(setComplexValue); const { uuid, linkedEntry } = entry; const linkedField = getLinkedField({ schema, linkedEntry }); diff --git a/src/common/hooks/useComplexLookupSearchResults.ts b/src/common/hooks/useComplexLookupSearchResults.ts index 474f839a..fb068384 100644 --- a/src/common/hooks/useComplexLookupSearchResults.ts +++ b/src/common/hooks/useComplexLookupSearchResults.ts @@ -1,10 +1,9 @@ import { useCallback, useMemo } from 'react'; -import { useRecoilValue } from 'recoil'; import { useIntl } from 'react-intl'; import { type Row } from '@components/Table'; import { useSearchContext } from '@common/hooks/useSearchContext'; import { ComplexLookupSearchResultsProps } from '@components/ComplexLookupField/ComplexLookupSearchResults'; -import state from '@state'; +import { useSearchState } from '@src/store'; export const useComplexLookupSearchResults = ({ onTitleClick, @@ -12,8 +11,7 @@ export const useComplexLookupSearchResults = ({ searchResultsFormatter, }: ComplexLookupSearchResultsProps) => { const { onAssignRecord } = useSearchContext(); - const data = useRecoilValue(state.search.data); - const sourceData = useRecoilValue(state.search.sourceData); + const { data, sourceData } = useSearchState(); const { formatMessage } = useIntl(); const applyActionItems = useCallback( diff --git a/src/common/hooks/useConfig.hook.ts b/src/common/hooks/useConfig.hook.ts index 97c40cf3..cca639f2 100644 --- a/src/common/hooks/useConfig.hook.ts +++ b/src/common/hooks/useConfig.hook.ts @@ -1,10 +1,9 @@ import { useRef } from 'react'; -import { useRecoilState, useSetRecoilState } from 'recoil'; import { v4 as uuidv4 } from 'uuid'; -import state from '@state'; import { fetchProfiles } from '@common/api/profiles.api'; import { PROFILE_NAMES } from '@common/constants/bibframe.constants'; import { getPrimaryEntitiesFromRecord, getRecordTitle } from '@common/helpers/record.helper'; +import { useInputsState, useProfileState } from '@src/store'; import { useProcessedRecordAndSchema } from './useProcessedRecordAndSchema.hook'; import { useServicesContext } from './useServicesContext'; @@ -31,15 +30,17 @@ type IBuildSchema = { export const useConfig = () => { const { schemaCreatorService, userValuesService, selectedEntriesService } = useServicesContext() as Required; - const [profiles, setProfiles] = useRecoilState(state.config.profiles); - const setSelectedProfile = useSetRecoilState(state.config.selectedProfile); - const setUserValues = useSetRecoilState(state.inputs.userValues); - const [preparedFields, setPreparedFields] = useRecoilState(state.config.preparedFields); - const setSchema = useSetRecoilState(state.config.schema); - const setInitialSchemaKey = useSetRecoilState(state.config.initialSchemaKey); - const setSelectedEntries = useSetRecoilState(state.config.selectedEntries); - const setPreviewContent = useSetRecoilState(state.inputs.previewContent); - const setSelectedRecordBlocks = useSetRecoilState(state.inputs.selectedRecordBlocks); + const { + profiles, + setProfiles, + setSelectedProfile, + preparedFields, + setPreparedFields, + setInitialSchemaKey, + setSchema, + } = useProfileState(); + const { setUserValues, previewContent, setPreviewContent, setSelectedRecordBlocks, setSelectedEntries } = + useInputsState(); const { getProcessedRecordAndSchema } = useProcessedRecordAndSchema(); const isProcessingProfiles = useRef(false); @@ -125,8 +126,8 @@ export const useConfig = () => { }); if (previewParams && recordId) { - setPreviewContent(prev => [ - ...(previewParams.singular ? [] : prev.filter(({ id }) => id !== recordId)), + setPreviewContent([ + ...(previewParams.singular ? [] : previewContent.filter(({ id }) => id !== recordId)), { id: recordId, base: updatedSchema, diff --git a/src/common/hooks/useContainerEvents.ts b/src/common/hooks/useContainerEvents.ts index e056310c..88691f7c 100644 --- a/src/common/hooks/useContainerEvents.ts +++ b/src/common/hooks/useContainerEvents.ts @@ -1,10 +1,9 @@ -import state from '@state'; -import { useRecoilValue } from 'recoil'; -import { IS_EMBEDDED_MODE } from '@common/constants/build.constants'; -import { dispatchEventWrapper, getWrapperAsWebComponent } from '@common/helpers/dom.helper'; import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; +import { IS_EMBEDDED_MODE } from '@common/constants/build.constants'; +import { dispatchEventWrapper, getWrapperAsWebComponent } from '@common/helpers/dom.helper'; import { ROUTES } from '@common/constants/routes.constants'; +import { useConfigState, useStatusState } from '@src/store'; type IUseContainerEvents = | { @@ -14,8 +13,9 @@ type IUseContainerEvents = | undefined; export const useContainerEvents = ({ onTriggerModal, watchEditedState = false }: IUseContainerEvents = {}) => { - const hasNavigationOrigin = useRecoilValue(state.config.hasNavigationOrigin); - const isEdited = useRecoilValue(state.status.recordIsEdited); + const { hasNavigationOrigin } = useConfigState(); + const { isRecordEdited: isEdited } = useStatusState(); + const { customEvents } = useConfigState(); const { BLOCK_NAVIGATION, UNBLOCK_NAVIGATION, @@ -23,7 +23,7 @@ export const useContainerEvents = ({ onTriggerModal, watchEditedState = false }: PROCEED_NAVIGATION, NAVIGATE_TO_ORIGIN, DROP_NAVIGATE_TO_ORIGIN, - } = useRecoilValue(state.config.customEvents) ?? {}; + } = customEvents ?? {}; const navigate = useNavigate(); useEffect(() => { diff --git a/src/common/hooks/useFetchSearchData.ts b/src/common/hooks/useFetchSearchData.ts index d5906841..7f0712a2 100644 --- a/src/common/hooks/useFetchSearchData.ts +++ b/src/common/hooks/useFetchSearchData.ts @@ -1,5 +1,4 @@ import { useCallback } from 'react'; -import { useSetRecoilState, useRecoilState, SetterOrUpdater, useResetRecoilState } from 'recoil'; import { getByIdentifier } from '@common/api/search.api'; import { SearchIdentifiers, SearchSegment } from '@common/constants/search.constants'; import { SearchableIndexQuerySelector } from '@common/constants/complexLookup.constants'; @@ -7,7 +6,7 @@ import { StatusType } from '@common/constants/status.constants'; import { normalizeQuery } from '@common/helpers/search.helper'; import { normalizeLccn } from '@common/helpers/validations.helper'; import { UserNotificationFactory } from '@common/services/userNotification'; -import state from '@state'; +import { useLoadingState, useSearchState, useStatusState } from '@src/store'; import { useSearchContext } from './useSearchContext'; export const useFetchSearchData = () => { @@ -24,13 +23,9 @@ export const useFetchSearchData = () => { buildSearchQuery, precedingRecordsCount, } = useSearchContext(); - const setIsLoading = useSetRecoilState(state.loadingState.isLoading); - const setMessage = useSetRecoilState(state.search.message); - const [data, setData] = useRecoilState(state.search.data); - const resetData = useResetRecoilState(state.search.data); - const setPageMetadata = useSetRecoilState(state.search.pageMetadata); - const setStatusMessages = useSetRecoilState(state.status.commonMessages); - const resetStatusMessage = useResetRecoilState(state.status.commonMessages); + const { setIsLoading } = useLoadingState(); + const { setMessage, data, setData, resetData, setPageMetadata } = useSearchState(); + const { addStatusMessagesItem, resetStatusMessages } = useStatusState(); const validateAndNormalizeQuery = useCallback( (type: SearchIdentifiers, query: string) => { @@ -130,13 +125,6 @@ export const useFetchSearchData = () => { }); }; - const handleFetchError = (setStatusMessages: SetterOrUpdater) => { - setStatusMessages(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching'), - ]); - }; - const fetchData = useCallback( async ({ query, @@ -145,7 +133,7 @@ export const useFetchSearchData = () => { selectedSegment, baseQuerySelector = SearchableIndexQuerySelector.Query, }: FetchDataParams) => { - resetStatusMessage(); + resetStatusMessages(); const selectedNavigationSegment = selectedSegment ?? navigationSegment?.value; data && resetData(); @@ -189,7 +177,7 @@ export const useFetchSearchData = () => { setData(content); setPageMetadata({ totalPages, totalElements: totalRecords, prev, next }); } catch { - handleFetchError(setStatusMessages); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching')); } finally { setIsLoading(false); } diff --git a/src/common/hooks/useLoadSearchResults.ts b/src/common/hooks/useLoadSearchResults.ts index eefaf5b1..0acd00a1 100644 --- a/src/common/hooks/useLoadSearchResults.ts +++ b/src/common/hooks/useLoadSearchResults.ts @@ -1,22 +1,17 @@ import { useEffect, useRef } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import { SearchQueryParams } from '@common/constants/routes.constants'; import { SEARCH_RESULTS_LIMIT, SearchIdentifiers } from '@common/constants/search.constants'; +import { useLoadingState, useSearchState } from '@src/store'; import { useSearchContext } from './useSearchContext'; -import state from '@state'; export const useLoadSearchResults = ( fetchData: ({ query, searchBy, offset, selectedSegment, baseQuerySelector }: FetchDataParams) => Promise, ) => { const { hasSearchParams, defaultSearchBy, defaultQuery, getSearchSourceData, getSearchFacetsData } = useSearchContext(); - const setData = useSetRecoilState(state.search.data); - const setSearchBy = useSetRecoilState(state.search.index); - const setIsLoading = useSetRecoilState(state.loadingState.isLoading); - const setQuery = useSetRecoilState(state.search.query); - const [forceRefresh, setForceRefresh] = useRecoilState(state.search.forceRefresh); - const resetFacetsData = useResetRecoilState(state.search.facetsData); + const { setIsLoading } = useLoadingState(); + const { setQuery, setData, setSearchBy, forceRefresh, setForceRefresh, resetFacetsData } = useSearchState(); const [searchParams] = useSearchParams(); const queryParam = searchParams.get(SearchQueryParams.Query); const searchByParam = searchParams.get(SearchQueryParams.SearchBy); diff --git a/src/common/hooks/useMarcData.ts b/src/common/hooks/useMarcData.ts index 5be3525a..5207dca2 100644 --- a/src/common/hooks/useMarcData.ts +++ b/src/common/hooks/useMarcData.ts @@ -1,14 +1,11 @@ -import { RecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import { getMarcRecord } from '@common/api/records.api'; import { StatusType } from '@common/constants/status.constants'; import { UserNotificationFactory } from '@common/services/userNotification'; -import state from '@state'; +import { useLoadingState, useStatusState } from '@src/store'; -export const useMarcData = (marcState: RecoilState) => { - const setMarcPreviewData = useSetRecoilState(marcState); - const clearMarcData = useResetRecoilState(marcState); - const setIsLoading = useSetRecoilState(state.loadingState.isLoading); - const setStatus = useSetRecoilState(state.status.commonMessages); +export const useMarcData = (setMarcPreviewData: (value: any) => void) => { + const { setIsLoading } = useLoadingState(); + const { addStatusMessagesItem } = useStatusState(); const fetchMarcData = async (recordId?: string, endpointUrl?: string): Promise => { if (!recordId) return undefined; @@ -22,10 +19,7 @@ export const useMarcData = (marcState: RecoilState) => { setMarcPreviewData(marcData); } catch (error) { - setStatus(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, 'ld.cantLoadMarc'), - ]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.cantLoadMarc')); } finally { setIsLoading(false); } @@ -33,5 +27,5 @@ export const useMarcData = (marcState: RecoilState) => { return marcData; }; - return { fetchMarcData, clearMarcData }; + return { fetchMarcData }; }; diff --git a/src/common/hooks/useNavigateToEditPage.ts b/src/common/hooks/useNavigateToEditPage.ts index 79670bc9..a458e520 100644 --- a/src/common/hooks/useNavigateToEditPage.ts +++ b/src/common/hooks/useNavigateToEditPage.ts @@ -1,11 +1,10 @@ import { ROUTES, QueryParams } from '@common/constants/routes.constants'; -import state from '@state'; +import { useSearchState } from '@src/store'; import { useNavigate } from 'react-router-dom'; -import { useRecoilValue } from 'recoil'; export const useNavigateToEditPage = () => { const navigate = useNavigate(); - const navigationState = useRecoilValue(state.search.navigationState); + const { navigationState } = useSearchState(); const navigateAsDuplicate = (duplicateId: string) => { navigate(`${ROUTES.RESOURCE_CREATE.uri}?${QueryParams.CloneOf}=${duplicateId}`, { state: navigationState }); diff --git a/src/common/hooks/usePagination.ts b/src/common/hooks/usePagination.ts index d28b8368..134e896c 100644 --- a/src/common/hooks/usePagination.ts +++ b/src/common/hooks/usePagination.ts @@ -1,13 +1,12 @@ import { useEffect, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { useRecoilValue } from 'recoil'; import { SearchQueryParams } from '@common/constants/routes.constants'; -import state from '@state'; +import { useSearchState } from '@src/store'; export const usePagination = (hasSearchParams = true, defaultPageNumber = 0, hasCycledPagination = false) => { const [searchParams, setSearchParams] = useSearchParams(); const offsetSearchParam = searchParams.get(SearchQueryParams.Offset); - const pageMetadata = useRecoilValue(state.search.pageMetadata); + const { pageMetadata } = useSearchState(); const [currentPageNumber, setCurrentPageNumber] = useState( offsetSearchParam ? parseInt(offsetSearchParam) : defaultPageNumber, ); diff --git a/src/common/hooks/useProcessedRecordAndSchema.hook.ts b/src/common/hooks/useProcessedRecordAndSchema.hook.ts index b817a5a8..deabe30b 100644 --- a/src/common/hooks/useProcessedRecordAndSchema.hook.ts +++ b/src/common/hooks/useProcessedRecordAndSchema.hook.ts @@ -1,5 +1,4 @@ import { useCallback } from 'react'; -import { useSetRecoilState } from 'recoil'; import { useIntl } from 'react-intl'; import { DUPLICATE_RESOURCE_TEMPLATE } from '@common/constants/resourceTemplates.constants'; import { @@ -10,8 +9,8 @@ import { import { applyIntlToTemplates } from '@common/helpers/recordFormatting.helper'; import { UserNotificationFactory } from '@common/services/userNotification'; import { StatusType } from '@common/constants/status.constants'; -import state from '@state'; import { useServicesContext } from './useServicesContext'; +import { useInputsState, useStatusState } from '@src/store'; type IGetProcessedRecordAndSchema = { baseSchema: Schema; @@ -22,8 +21,8 @@ type IGetProcessedRecordAndSchema = { }; export const useProcessedRecordAndSchema = () => { - const setRecord = useSetRecoilState(state.inputs.record); - const setStatusMessages = useSetRecoilState(state.status.commonMessages); + const { addStatusMessagesItem } = useStatusState(); + const { setRecord } = useInputsState(); const { formatMessage } = useIntl(); const { userValuesService, schemaWithDuplicatesService, recordNormalizingService, recordToSchemaMappingService } = useServicesContext() as Required; @@ -73,10 +72,7 @@ export const useProcessedRecordAndSchema = () => { } catch (error) { console.error(error); - setStatusMessages(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, 'ld.errorLoadingResource'), - ]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.errorLoadingResource')); } return { @@ -91,7 +87,7 @@ export const useProcessedRecordAndSchema = () => { recordToSchemaMappingService, schemaWithDuplicatesService, setRecord, - setStatusMessages, + addStatusMessagesItem, userValuesService, ], ); diff --git a/src/common/hooks/useProfileSchema.ts b/src/common/hooks/useProfileSchema.ts index e87075bf..9daa48ae 100644 --- a/src/common/hooks/useProfileSchema.ts +++ b/src/common/hooks/useProfileSchema.ts @@ -1,15 +1,13 @@ -import { useRecoilState, useSetRecoilState } from 'recoil'; -import state from '@state'; import { useServicesContext } from './useServicesContext'; import { deleteFromSetImmutable } from '@common/helpers/common.helper'; +import { useInputsState, useProfileState, useStatusState, useUIState } from '@src/store'; export const useProfileSchema = () => { const { selectedEntriesService, schemaWithDuplicatesService } = useServicesContext() as Required; - const [schema, setSchema] = useRecoilState(state.config.schema); - const setSelectedEntries = useSetRecoilState(state.config.selectedEntries); - const setCollapsibleEntries = useSetRecoilState(state.ui.collapsibleEntries); - const setIsEdited = useSetRecoilState(state.status.recordIsEdited); - const setUserValues = useSetRecoilState(state.inputs.userValues); + const { setCollapsibleEntries } = useUIState(); + const { setUserValues, setSelectedEntries } = useInputsState(); + const { setIsRecordEdited: setIsEdited } = useStatusState(); + const { schema, setSchema } = useProfileState(); const getSchemaWithCopiedEntries = (entry: SchemaEntry, selectedEntries: string[]) => { selectedEntriesService.set(selectedEntries); diff --git a/src/common/hooks/useRecordControls.ts b/src/common/hooks/useRecordControls.ts index 1b440858..97f3abdf 100644 --- a/src/common/hooks/useRecordControls.ts +++ b/src/common/hooks/useRecordControls.ts @@ -1,6 +1,5 @@ import { flushSync } from 'react-dom'; import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'; -import { useRecoilState, useSetRecoilState } from 'recoil'; import { postRecord, putRecord, @@ -26,12 +25,12 @@ import { QueryParams, ROUTES } from '@common/constants/routes.constants'; import { BLOCKS_BFLITE } from '@common/constants/bibframeMapping.constants'; import { RecordStatus, ResourceType } from '@common/constants/record.constants'; import { generateEditResourceUrl } from '@common/helpers/navigation.helper'; -import { useBackToSearchUri } from './useBackToSearchUri'; -import state from '@state'; -import { useContainerEvents } from './useContainerEvents'; import { ApiErrorCodes, ExternalResourceIdType } from '@common/constants/api.constants'; import { checkHasErrorOfCodeType } from '@common/helpers/api.helper'; +import { useLoadingState, useStatusState, useProfileState, useInputsState, useUIState } from '@src/store'; import { useRecordGeneration } from './useRecordGeneration'; +import { useBackToSearchUri } from './useBackToSearchUri'; +import { useContainerEvents } from './useContainerEvents'; type SaveRecordProps = { asRefToNewRecord?: boolean; @@ -49,18 +48,12 @@ type IBaseFetchRecord = { export const useRecordControls = () => { const [searchParams, setSearchParams] = useSearchParams(); - const setIsLoading = useSetRecoilState(state.loadingState.isLoading); - const setUserValues = useSetRecoilState(state.inputs.userValues); - const setSelectedProfile = useSetRecoilState(state.config.selectedProfile); - const [record, setRecord] = useRecoilState(state.inputs.record); - const setIsEdited = useSetRecoilState(state.status.recordIsEdited); - const setRecordStatus = useSetRecoilState(state.status.recordStatus); - const setLastSavedRecordId = useSetRecoilState(state.status.lastSavedRecordId); - const setStatusMessages = useSetRecoilState(state.status.commonMessages); - const setCurrentlyEditedEntityBfid = useSetRecoilState(state.ui.currentlyEditedEntityBfid); - const setCurrentlyPreviewedEntityBfid = useSetRecoilState(state.ui.currentlyPreviewedEntityBfid); - const [selectedRecordBlocks, setSelectedRecordBlocks] = useRecoilState(state.inputs.selectedRecordBlocks); - const setIsDuplicateImportedResourceModalOpen = useSetRecoilState(state.ui.isDuplicateImportedResourceModalOpen); + const { setIsLoading } = useLoadingState(); + const { resetUserValues, selectedRecordBlocks, setSelectedRecordBlocks, record, setRecord } = useInputsState(); + const { setSelectedProfile } = useProfileState(); + const { setIsDuplicateImportedResourceModalOpen, setCurrentlyEditedEntityBfid, setCurrentlyPreviewedEntityBfid } = + useUIState(); + const { setRecordStatus, setLastSavedRecordId, setIsRecordEdited: setIsEdited, addStatusMessagesItem } = useStatusState(); const profile = PROFILE_BFIDS.MONOGRAPH; const currentRecordId = getRecordId(record); const { getProfiles } = useConfig(); @@ -131,10 +124,9 @@ export const useRecordControls = () => { dispatchUnblockEvent(); !asRefToNewRecord && setRecord(parsedResponse); - setStatusMessages(currentStatus => [ - ...currentStatus, + addStatusMessagesItem?.( UserNotificationFactory.createMessage(StatusType.success, recordId ? 'ld.rdUpdateSuccess' : 'ld.rdSaveSuccess'), - ]); + ); // isEdited state update is not immediately reflected in the // blocker component, forcing to block the navigation call below @@ -179,10 +171,7 @@ export const useRecordControls = () => { } catch (error) { console.error('Cannot save the resource description', error); - setStatusMessages(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, 'ld.cantSaveRd'), - ]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.cantSaveRd')); } finally { setIsLoading(false); } @@ -197,7 +186,7 @@ export const useRecordControls = () => { }; const clearRecordState = () => { - setUserValues({}); + resetUserValues(); setRecord(null); setSelectedRecordBlocks(undefined); setSelectedProfile(null); @@ -218,19 +207,13 @@ export const useRecordControls = () => { await deleteRecordRequest(currentRecordId as unknown as string); deleteRecordLocally(profile, currentRecordId as unknown as string); discardRecord(); - setStatusMessages(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.success, 'ld.rdDeleted'), - ]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.success, 'ld.rdDeleted')); navigate(ROUTES.SEARCH.uri); } catch (error) { console.error('Cannot delete the resource description', error); - setStatusMessages(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, 'ld.cantDeleteRd'), - ]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.cantDeleteRd')); } }; @@ -241,10 +224,7 @@ export const useRecordControls = () => { const contents = record?.resource?.[uriSelector]; if (!contents) { - setStatusMessages(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, 'ld.cantSelectReferenceContents'), - ]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.cantSelectReferenceContents')); return navigate(ROUTES.RESOURCE_CREATE.uri); } @@ -264,10 +244,7 @@ export const useRecordControls = () => { } catch (e) { console.error('Error fetching record and selecting entity values: ', e); - setStatusMessages(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching'), - ]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching')); } }; @@ -285,10 +262,7 @@ export const useRecordControls = () => { return recordData; } catch (_err) { - setStatusMessages(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, errorMessage ?? 'ld.errorFetching'), - ]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, errorMessage ?? 'ld.errorFetching')); } }; @@ -315,10 +289,9 @@ export const useRecordControls = () => { if (checkHasErrorOfCodeType(err as ApiError, ApiErrorCodes.AlreadyExists)) { setIsDuplicateImportedResourceModalOpen(true); } else { - setStatusMessages(currentStatus => [ - ...currentStatus, + addStatusMessagesItem?.( UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetchingExternalResourceForEditing'), - ]); + ); } } finally { setIsLoading(false); diff --git a/src/common/hooks/useRecordGeneration.ts b/src/common/hooks/useRecordGeneration.ts index 4495f3d3..b14ef6f6 100644 --- a/src/common/hooks/useRecordGeneration.ts +++ b/src/common/hooks/useRecordGeneration.ts @@ -1,13 +1,10 @@ -import { useRecoilValue } from 'recoil'; +import { useInputsState, useProfileState } from '@src/store'; import { useServicesContext } from './useServicesContext'; -import state from '@state'; export const useRecordGeneration = () => { const { recordGeneratorService } = useServicesContext(); - const schema = useRecoilValue(state.config.schema); - const userValues = useRecoilValue(state.inputs.userValues); - const selectedEntries = useRecoilValue(state.config.selectedEntries); - const initialSchemaKey = useRecoilValue(state.config.initialSchemaKey); + const { userValues, selectedEntries } = useInputsState(); + const { schema, initialSchemaKey } = useProfileState(); const generateRecord = () => { recordGeneratorService?.init({ diff --git a/src/common/hooks/useRecordStatus.ts b/src/common/hooks/useRecordStatus.ts index 26afced1..da77ad15 100644 --- a/src/common/hooks/useRecordStatus.ts +++ b/src/common/hooks/useRecordStatus.ts @@ -1,9 +1,8 @@ -import state from '@state'; +import { useStatusState } from '@src/store'; import { useParams } from 'react-router-dom'; -import { useRecoilValue } from 'recoil'; export const useRecordStatus = () => { - const lastSavedRecordId = useRecoilValue(state.status.lastSavedRecordId); + const { lastSavedRecordId } = useStatusState(); const { resourceId } = useParams(); const hasBeenSaved = resourceId && resourceId === lastSavedRecordId; diff --git a/src/common/hooks/useResetRecordStatus.ts b/src/common/hooks/useResetRecordStatus.ts index 3a6ba4f6..44f302ea 100644 --- a/src/common/hooks/useResetRecordStatus.ts +++ b/src/common/hooks/useResetRecordStatus.ts @@ -1,11 +1,10 @@ -import { RecordStatus } from '@common/constants/record.constants'; -import state from '@state'; import { useState, useEffect } from 'react'; import { useParams } from 'react-router-dom'; -import { useRecoilState } from 'recoil'; +import { RecordStatus } from '@common/constants/record.constants'; +import { useStatusState } from '@src/store'; export const useResetRecordStatus = () => { - const [recordStatus, setRecordStatus] = useRecoilState(state.status.recordStatus); + const { recordStatus, setRecordStatus } = useStatusState(); const [prevResourceId, setPrevResourceId] = useState(null); const { resourceId } = useParams(); const setRecordStatusAsOpen = () => setRecordStatus({ type: RecordStatus.open }); diff --git a/src/common/hooks/useSearch.ts b/src/common/hooks/useSearch.ts index 52fd01cb..5cac6d9d 100644 --- a/src/common/hooks/useSearch.ts +++ b/src/common/hooks/useSearch.ts @@ -1,6 +1,5 @@ import { useCallback, useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { useSetRecoilState, useRecoilState, useResetRecoilState } from 'recoil'; import { SearchableIndexQuerySelector } from '@common/constants/complexLookup.constants'; import { DEFAULT_PAGES_METADATA } from '@common/constants/api.constants'; import { SearchIdentifiers, SearchSegment } from '@common/constants/search.constants'; @@ -8,7 +7,7 @@ import { generateSearchParamsState } from '@common/helpers/search.helper'; import { usePagination } from '@common/hooks/usePagination'; import { useSearchContext } from '@common/hooks/useSearchContext'; import { useFetchSearchData } from '@common/hooks/useFetchSearchData'; -import state from '@state'; +import { useInputsState, useLoadingState, useSearchState } from '@src/store'; export const useSearch = () => { const { @@ -20,17 +19,26 @@ export const useSearch = () => { searchByControlOptions, getSearchSourceData, } = useSearchContext(); - const setIsLoading = useSetRecoilState(state.loadingState.isLoading); - const [searchBy, setSearchBy] = useRecoilState(state.search.index); - const [query, setQuery] = useRecoilState(state.search.query); - const [facets, setFacets] = useRecoilState(state.search.limiters); - const [message, setMessage] = useRecoilState(state.search.message); - const [data, setData] = useRecoilState(state.search.data); - const [pageMetadata, setPageMetadata] = useRecoilState(state.search.pageMetadata); - const setForceRefreshSearch = useSetRecoilState(state.search.forceRefresh); - const resetPreviewContent = useResetRecoilState(state.inputs.previewContent); - const [facetsBySegments, setFacetsBySegments] = useRecoilState(state.search.facetsBySegments); - const clearFacetsBySegments = useResetRecoilState(state.search.facetsBySegments); + const { setIsLoading } = useLoadingState(); + const { resetPreviewContent } = useInputsState(); + const { + searchBy, + setSearchBy, + query, + setQuery, + facets, + setFacets, + message, + setMessage, + data, + setData, + pageMetadata, + setPageMetadata, + setForceRefresh: setForceRefreshSearch, + facetsBySegments, + setFacetsBySegments, + resetFacetsBySegments, + } = useSearchState(); const { fetchData } = useFetchSearchData(); const { @@ -198,8 +206,8 @@ export const useSearch = () => { // Reset Segments selection on unmount useEffect(() => { - return clearFacetsBySegments; - }, [clearFacetsBySegments]); + return resetFacetsBySegments; + }, [resetFacetsBySegments]); return { submitSearch, diff --git a/src/common/hooks/useSearchFilterLookupOptions.ts b/src/common/hooks/useSearchFilterLookupOptions.ts index c042725f..82e28362 100644 --- a/src/common/hooks/useSearchFilterLookupOptions.ts +++ b/src/common/hooks/useSearchFilterLookupOptions.ts @@ -1,6 +1,5 @@ -import { useRecoilValue } from 'recoil'; import { useIntl } from 'react-intl'; -import state from '@state'; +import { useSearchState } from '@src/store'; export const useSearchFilterLookupOptions = ({ facet, @@ -10,8 +9,7 @@ export const useSearchFilterLookupOptions = ({ hasMappedSourceData?: boolean; excludedOptions?: string[]; }) => { - const sourceData = useRecoilValue(state.search.sourceData); - const facetsData = useRecoilValue(state.search.facetsData); + const { sourceData, facetsData } = useSearchState(); const { formatMessage } = useIntl(); const facetValues = facet && facetsData ? facetsData?.[facet]?.values : undefined; diff --git a/src/common/hooks/useSearchFilters.ts b/src/common/hooks/useSearchFilters.ts index 626ea7c5..e560de69 100644 --- a/src/common/hooks/useSearchFilters.ts +++ b/src/common/hooks/useSearchFilters.ts @@ -1,20 +1,19 @@ import { ChangeEvent } from 'react'; -import { useRecoilState } from 'recoil'; import { SearchLimiterNames } from '@common/constants/search.constants'; -import state from '@state'; +import { useSearchState } from '@src/store'; export const useSearchFilters = () => { - const [limiters, setLimiters] = useRecoilState(state.search.limiters); + const { facets, setFacets } = useSearchState(); const onChangeLimiters = ({ target: { id, name } }: ChangeEvent) => { - setLimiters(prev => ({ + setFacets(prev => ({ ...prev, [name]: id, })); }; const onChangeLimitersMulti = ({ target: { id, name } }: ChangeEvent) => { - setLimiters(prev => { + setFacets(prev => { const initialLimiters = (prev[name as SearchLimiterNames] as any[]) || []; return { @@ -29,7 +28,7 @@ export const useSearchFilters = () => { const onChange = (_e: ChangeEvent) => {}; return { - limiters, + facets, onChangeLimiters, onChangeLimitersMulti, onChange, diff --git a/src/common/hooks/useSearchFiltersData.ts b/src/common/hooks/useSearchFiltersData.ts index aa62b756..1ba88b08 100644 --- a/src/common/hooks/useSearchFiltersData.ts +++ b/src/common/hooks/useSearchFiltersData.ts @@ -1,19 +1,16 @@ import { useEffect } from 'react'; -import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import { StatusType } from '@common/constants/status.constants'; import { UserNotificationFactory } from '@common/services/userNotification'; import * as SearchApi from '@common/api/search.api'; -import state from '@state'; +import { useSearchState, useStatusState } from '@src/store'; const DEFAULT_SEARCH_SOURCE_LIMIT = '50'; const DEFAULT_SEARCH_FACETS_QUERY = 'id=*'; export const useSearchFiltersData = () => { - const [selectedFacetsGroups, setSelectedFacetsGroups] = useRecoilState(state.search.selectedFacetsGroups); - const resetSelectedFacetsGroups = useResetRecoilState(state.search.selectedFacetsGroups); - const setFacetsData = useSetRecoilState(state.search.facetsData); - const setSourceData = useSetRecoilState(state.search.sourceData); - const setCommonStatus = useSetRecoilState(state.status.commonMessages); + const { selectedFacetsGroups, setSelectedFacetsGroups, resetSelectedFacetsGroups, setFacetsData, setSourceData } = + useSearchState(); + const { addStatusMessagesItem } = useStatusState(); useEffect(() => { return resetSelectedFacetsGroups(); @@ -40,10 +37,7 @@ export const useSearchFiltersData = () => { } catch (error) { console.error(error); - setCommonStatus(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching'), - ]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching')); } }; @@ -62,10 +56,7 @@ export const useSearchFiltersData = () => { } catch (error) { console.error(error); - setCommonStatus(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching'), - ]); + addStatusMessagesItem?.(UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching')); } }; diff --git a/src/common/hooks/useSearchNavigationState.ts b/src/common/hooks/useSearchNavigationState.ts index c5879d05..7a083698 100644 --- a/src/common/hooks/useSearchNavigationState.ts +++ b/src/common/hooks/useSearchNavigationState.ts @@ -1,9 +1,8 @@ import { useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { useSetRecoilState } from 'recoil'; import { SearchQueryParams } from '@common/constants/routes.constants'; import { generateSearchParamsState } from '@common/helpers/search.helper'; -import state from '@state'; +import { useSearchState } from '@src/store'; export const useSearchNavigationState = () => { const [searchParams] = useSearchParams(); @@ -11,7 +10,7 @@ export const useSearchNavigationState = () => { const searchBySearchParam = searchParams.get(SearchQueryParams.SearchBy); const offsetSearchParam = searchParams.get(SearchQueryParams.Offset); - const setNavigationState = useSetRecoilState(state.search.navigationState); + const { setNavigationState } = useSearchState(); useEffect(() => { const generatedState = generateSearchParamsState( diff --git a/src/components/AdvancedSearchModal/AdvancedSearchModal.tsx b/src/components/AdvancedSearchModal/AdvancedSearchModal.tsx index ccf64153..332cb90b 100644 --- a/src/components/AdvancedSearchModal/AdvancedSearchModal.tsx +++ b/src/components/AdvancedSearchModal/AdvancedSearchModal.tsx @@ -1,6 +1,5 @@ import { FC, memo, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { useRecoilState, useSetRecoilState } from 'recoil'; import { FormattedMessage, useIntl } from 'react-intl'; import { Modal } from '@components/Modal'; import { Input } from '@components/Input'; @@ -12,7 +11,7 @@ import { } from '@common/constants/search.constants'; import { formatRawQuery, generateSearchParamsState } from '@common/helpers/search.helper'; import { Select } from '@components/Select'; -import state from '@state'; +import { useSearchState, useUIState } from '@src/store'; import { AriaModalKind } from '@common/constants/uiElements.constants'; import './AdvancedSearchModal.scss'; @@ -30,8 +29,8 @@ type Props = { export const AdvancedSearchModal: FC = memo(({ clearValues }) => { const [, setSearchParams] = useSearchParams(); const { formatMessage } = useIntl(); - const [isOpen, setIsOpen] = useRecoilState(state.ui.isAdvancedSearchOpen); - const setForceRefreshSearch = useSetRecoilState(state.search.forceRefresh); + const { isAdvancedSearchOpen: isOpen, setIsAdvancedSearchOpen: setIsOpen } = useUIState(); + const { setForceRefresh: setForceRefreshSearch } = useSearchState(); const [rawQuery, setRawQuery] = useState(DEFAULT_ADVANCED_SEARCH_QUERY); const closeModal = () => { diff --git a/src/components/Announcement/index.ts b/src/components/Announcement/index.ts new file mode 100644 index 00000000..e0f8e918 --- /dev/null +++ b/src/components/Announcement/index.ts @@ -0,0 +1 @@ +export { Announcement } from './Announcement'; diff --git a/src/components/CommonStatus/CommonStatus.tsx b/src/components/CommonStatus/CommonStatus.tsx index 1facabba..d1078e91 100644 --- a/src/components/CommonStatus/CommonStatus.tsx +++ b/src/components/CommonStatus/CommonStatus.tsx @@ -1,10 +1,9 @@ import { FC, useEffect } from 'react'; -import { useRecoilState } from 'recoil'; import classNames from 'classnames'; import { FormattedMessage } from 'react-intl'; -import state from '@state'; import { StatusType } from '@common/constants/status.constants'; import { Button, ButtonType } from '@components/Button'; +import { useStatusState } from '@src/store'; import CloseIcon from '@src/assets/times-16.svg?react'; import CheckCircleIcon from '@src/assets/check-circle.svg?react'; import WarningIcon from '@src/assets/exclamation-triangle.svg?react'; @@ -14,10 +13,10 @@ import './CommonStatus.scss'; const DELETE_TIMEOUT = 10000; export const CommonStatus: FC = () => { - const [statusMessages, setStatusMessages] = useRecoilState(state.status.commonMessages); + const { statusMessages, setStatusMessages } = useStatusState(); const deleteMessage = (messageId?: string) => { - setStatusMessages(current => current.filter(({ id }) => id !== messageId)); + setStatusMessages(prev => prev.filter(({ id }) => id !== messageId)); }; const deleteOldestMessage = () => deleteMessage(statusMessages[0].id); diff --git a/src/components/ComplexLookupField/MarcPreviewComplexLookup.tsx b/src/components/ComplexLookupField/MarcPreviewComplexLookup.tsx index 3cef0f8f..14116675 100644 --- a/src/components/ComplexLookupField/MarcPreviewComplexLookup.tsx +++ b/src/components/ComplexLookupField/MarcPreviewComplexLookup.tsx @@ -1,12 +1,11 @@ import { FC } from 'react'; -import { useRecoilValue } from 'recoil'; import { FormattedDate, FormattedMessage, useIntl } from 'react-intl'; import { useSearchContext } from '@common/hooks/useSearchContext'; +import { useMarcPreviewState, useUIState } from '@src/store'; import { SearchControlPane } from '@components/SearchControlPane'; import { MarcContent } from '@components/MarcContent'; import { Button, ButtonType } from '@components/Button'; import Times16 from '@src/assets/times-16.svg?react'; -import state from '@state'; import './MarcPreviewComplexLookup.scss'; type MarcPreviewComplexLookupProps = { @@ -16,9 +15,8 @@ type MarcPreviewComplexLookupProps = { export const MarcPreviewComplexLookup: FC = ({ onClose }) => { const { onAssignRecord } = useSearchContext(); const { formatMessage } = useIntl(); - const isMarcPreviewOpen = useRecoilValue(state.ui.isMarcPreviewOpen); - const marcPreviewData = useRecoilValue(state.data.marcPreviewData); - const marcPreviewMetadata = useRecoilValue(state.data.marcPreviewMetadata); + const { isMarcPreviewOpen } = useUIState(); + const { complexValue: marcPreviewData, metadata: marcPreviewMetadata } = useMarcPreviewState(); const renderCloseButton = () => ( diff --git a/src/components/SearchControlPane/SearchControlPane.tsx b/src/components/SearchControlPane/SearchControlPane.tsx index e1fa6063..c37d4f9e 100644 --- a/src/components/SearchControlPane/SearchControlPane.tsx +++ b/src/components/SearchControlPane/SearchControlPane.tsx @@ -1,10 +1,9 @@ import { FC } from 'react'; -import { useRecoilValue } from 'recoil'; import classNames from 'classnames'; import { IS_EMBEDDED_MODE } from '@common/constants/build.constants'; import { useSearchContext } from '@common/hooks/useSearchContext'; import { SearchSegment } from '@common/constants/search.constants'; -import state from '@state'; +import { useSearchState } from '@src/store'; import './SearchControlPane.scss'; type SearchControlPaneProps = { @@ -22,7 +21,7 @@ export const SearchControlPane: FC = ({ renderCloseButton, segmentsConfig, }) => { - const searchResultsMetadata = useRecoilValue(state.search.pageMetadata); + const { pageMetadata: searchResultsMetadata } = useSearchState(); const { navigationSegment } = useSearchContext(); const selectedSegment = navigationSegment?.value; const isVisibleSubLabel = segmentsConfig diff --git a/src/components/SearchControls/SearchControls.tsx b/src/components/SearchControls/SearchControls.tsx index 97a547c3..7e6e2140 100644 --- a/src/components/SearchControls/SearchControls.tsx +++ b/src/components/SearchControls/SearchControls.tsx @@ -1,6 +1,5 @@ import { ChangeEvent, FC, FormEventHandler, useEffect, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import { FormattedMessage, useIntl } from 'react-intl'; import classNames from 'classnames'; import { DEFAULT_FACET_BY_SEGMENT, SearchIdentifiers } from '@common/constants/search.constants'; @@ -11,12 +10,12 @@ import { Input } from '@components/Input'; import { Select } from '@components/Select'; import { SearchFilters } from '@components/SearchFilters'; import { Textarea } from '@components/Textarea'; +import { Announcement } from '@components/Announcement'; +import { useSearchState, useUIState } from '@src/store'; import SearchSegments from './SearchSegments'; -import state from '@state'; import CaretDown from '@src/assets/caret-down.svg?react'; import XInCircle from '@src/assets/x-in-circle.svg?react'; import './SearchControls.scss'; -import { Announcement } from '@components/Announcement/Announcement'; type Props = { submitSearch: VoidFunction; @@ -37,13 +36,18 @@ export const SearchControls: FC = ({ submitSearch, changeSegment, clearVa defaultSearchBy, navigationSegment, } = useSearchContext(); - const [searchBy, setSearchBy] = useRecoilState(state.search.index); - const [query, setQuery] = useRecoilState(state.search.query); - const setMessage = useSetRecoilState(state.search.message); - const setNavigationState = useSetRecoilState(state.search.navigationState); - const resetControls = useResetRecoilState(state.search.limiters); - const setIsAdvancedSearchOpen = useSetRecoilState(state.ui.isAdvancedSearchOpen); - const setFacetsBySegments = useSetRecoilState(state.search.facetsBySegments); + + const { + searchBy, + setSearchBy, + query, + setQuery, + setMessage, + setNavigationState, + resetFacets: resetControls, + setFacetsBySegments, + } = useSearchState(); + const { setIsAdvancedSearchOpen } = useUIState(); const [searchParams, setSearchParams] = useSearchParams(); const [announcementMessage, setAnnouncementMessage] = useState(''); const searchQueryParam = searchParams.get(SearchQueryParams.Query); @@ -144,10 +148,7 @@ export const SearchControls: FC = ({ submitSearch, changeSegment, clearVa > - setAnnouncementMessage('')} - /> + setAnnouncementMessage('')} /> {isVisibleAdvancedSearch && (