Skip to content

Commit

Permalink
migrate loading state
Browse files Browse the repository at this point in the history
  • Loading branch information
SKarolFolio committed Dec 2, 2024
1 parent 2152a97 commit 6afd2d6
Show file tree
Hide file tree
Showing 19 changed files with 63 additions and 53 deletions.
4 changes: 2 additions & 2 deletions src/common/hooks/useFetchSearchData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ 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 { useLoadingState, useStatusState } from '@src/store';
import state from '@state';
import { useSearchContext } from './useSearchContext';
import { useStatusState } from '@src/store';

export const useFetchSearchData = () => {
const {
Expand All @@ -25,7 +25,7 @@ export const useFetchSearchData = () => {
buildSearchQuery,
precedingRecordsCount,
} = useSearchContext();
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const { setIsLoading } = useLoadingState();
const setMessage = useSetRecoilState(state.search.message);
const [data, setData] = useRecoilState(state.search.data);
const resetData = useResetRecoilState(state.search.data);
Expand Down
5 changes: 3 additions & 2 deletions src/common/hooks/useLoadSearchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ 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 { useSearchContext } from './useSearchContext';
import { useLoadingState } from '@src/store';
import state from '@state';
import { useSearchContext } from './useSearchContext';

export const useLoadSearchResults = (
fetchData: ({ query, searchBy, offset, selectedSegment, baseQuerySelector }: FetchDataParams) => Promise<void>,
Expand All @@ -13,7 +14,7 @@ export const useLoadSearchResults = (
useSearchContext();
const setData = useSetRecoilState(state.search.data);
const setSearchBy = useSetRecoilState(state.search.index);
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const { setIsLoading } = useLoadingState();
const setQuery = useSetRecoilState(state.search.query);
const [forceRefresh, setForceRefresh] = useRecoilState(state.search.forceRefresh);
const resetFacetsData = useResetRecoilState(state.search.facetsData);
Expand Down
6 changes: 2 additions & 4 deletions src/common/hooks/useMarcData.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { useSetRecoilState } from 'recoil';
import { getMarcRecord } from '@common/api/records.api';
import { StatusType } from '@common/constants/status.constants';
import { UserNotificationFactory } from '@common/services/userNotification';
import { useStatusState } from '@src/store';
import state from '@state';
import { useLoadingState, useStatusState } from '@src/store';

export const useMarcData = (setMarcPreviewData: (value: any) => void) => {
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const { setIsLoading } = useLoadingState();
const { addStatusMessage } = useStatusState();

const fetchMarcData = async (recordId?: string, endpointUrl?: string): Promise<MarcDTO | undefined> => {
Expand Down
10 changes: 5 additions & 5 deletions src/common/hooks/useRecordControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ 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 } from '@src/store';
import state from '@state';
import { useRecordGeneration } from './useRecordGeneration';
import { useStatusState } from '@src/store';
import { useBackToSearchUri } from './useBackToSearchUri';
import { useContainerEvents } from './useContainerEvents';

type SaveRecordProps = {
asRefToNewRecord?: boolean;
Expand All @@ -49,7 +49,7 @@ type IBaseFetchRecord = {

export const useRecordControls = () => {
const [searchParams, setSearchParams] = useSearchParams();
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const { setIsLoading } = useLoadingState();
const setUserValues = useSetRecoilState(state.inputs.userValues);
const setSelectedProfile = useSetRecoilState(state.config.selectedProfile);
const [record, setRecord] = useRecoilState(state.inputs.record);
Expand Down
3 changes: 2 additions & 1 deletion src/common/hooks/useSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,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 { useLoadingState } from '@src/store';
import state from '@state';

export const useSearch = () => {
Expand All @@ -20,7 +21,7 @@ export const useSearch = () => {
searchByControlOptions,
getSearchSourceData,
} = useSearchContext();
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const { setIsLoading } = useLoadingState();
const [searchBy, setSearchBy] = useRecoilState(state.search.index);
const [query, setQuery] = useRecoilState(state.search.query);
const [facets, setFacets] = useRecoilState(state.search.limiters);
Expand Down
4 changes: 2 additions & 2 deletions src/components/EditControlPane/EditControlPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useRoutePathPattern } from '@common/hooks/useRoutePathPattern';
import { useNavigateToEditPage } from '@common/hooks/useNavigateToEditPage';
import { useMarcData } from '@common/hooks/useMarcData';
import { getEditActionPrefix } from '@common/helpers/bibframe.helper';
import { useMarcPreviewState, useStatusState } from '@src/store';
import { useLoadingState, useMarcPreviewState, useStatusState } from '@src/store';
import state from '@state';
import EyeOpen16 from '@src/assets/eye-open-16.svg?react';
import ExternalLink16 from '@src/assets/external-link-16.svg?react';
Expand All @@ -23,7 +23,7 @@ import './EditControlPane.scss';

export const EditControlPane = () => {
const isInCreateMode = useRoutePathPattern(RESOURCE_CREATE_URLS);
const isLoading = useRecoilValue(state.loadingState.isLoading);
const { isLoading } = useLoadingState();
const currentlyEditedEntityBfid = useRecoilValue(state.ui.currentlyEditedEntityBfid);
const { setRecordStatus } = useStatusState();
const { setBasicValue } = useMarcPreviewState();
Expand Down
6 changes: 3 additions & 3 deletions src/components/SearchResultEntry/SearchResultEntry.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC, useState } from 'react';
import { Link } from 'react-router-dom';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useRecoilValue } from 'recoil';
import classNames from 'classnames';
import { FormattedMessage } from 'react-intl';
import { WorkDetailsCard } from '@components/WorkDetailsCard';
Expand All @@ -17,8 +17,8 @@ import CommentIcon from '@src/assets/comment-lines-12.svg?react';
import { useRecordControls } from '@common/hooks/useRecordControls';
import { UserNotificationFactory } from '@common/services/userNotification';
import { StatusType } from '@common/constants/status.constants';
import { useLoadingState, useStatusState } from '@src/store';
import './SearchResultEntry.scss';
import { useStatusState } from '@src/store';

type SearchResultEntry = {
id: string;
Expand Down Expand Up @@ -61,7 +61,7 @@ export const SearchResultEntry: FC<SearchResultEntry> = ({ instances, ...restOfW
const { navigateToEditPage } = useNavigateToEditPage();
const navigationState = useRecoilValue(state.search.navigationState);
const [isOpen, setIsOpen] = useState(true);
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const { setIsLoading } = useLoadingState();
const { addStatusMessage } = useStatusState();
const previewContent = useRecoilValue(state.inputs.previewContent);
const toggleIsOpen = () => setIsOpen(!isOpen);
Expand Down
6 changes: 3 additions & 3 deletions src/components/ViewMarcControlPane/ViewMarcControlPane.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useRecoilValue } from 'recoil';
import { FormattedMessage, useIntl } from 'react-intl';
import { Button, ButtonType } from '@components/Button';
import state from '@state';
import { useRecoilValue } from 'recoil';
import Times16 from '@src/assets/times-16.svg?react';
import { getRecordTitle } from '@common/helpers/record.helper';
import { useMarcPreviewState } from '@src/store';
import { useLoadingState, useMarcPreviewState } from '@src/store';

export const ViewMarcControlPane = () => {
const isLoading = useRecoilValue(state.loadingState.isLoading);
const { isLoading } = useLoadingState();
const { resetBasicValue: resetMarcPreviewData } = useMarcPreviewState();
const record = useRecoilValue(state.inputs.record);
const { formatMessage } = useIntl();
Expand Down
2 changes: 0 additions & 2 deletions src/state/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import config from './config';
import inputs from './inputs';
import ui from './ui';
import loadingState from './loadingState';
import search from './search';

export default {
config,
inputs,
ui,
loadingState,
search,
};
10 changes: 0 additions & 10 deletions src/state/loadingState.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './status';
export * from './loadingState';
export * from './marcPreview';
export * from './selectors';
17 changes: 17 additions & 0 deletions src/store/loadingState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import { IS_PROD_MODE } from '@common/constants/bundle.constants';
import { createBaseSlice, SliceState } from './utils/slice';

export type LoadingState = SliceState<'isLoading', boolean>;

const STORE_NAME = 'LoadingState';

export const useLoadingStateStore = create<LoadingState>()(
devtools(
(...args) => ({
...createBaseSlice({ basic: 'isLoading' }, false, STORE_NAME)(...args),
}),
{ enabled: !IS_PROD_MODE },
),
);
2 changes: 2 additions & 0 deletions src/store/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createSelectors } from './utils/selectors';
import { useStatusStore } from './status';
import { useLoadingStateStore } from './loadingState';
import { useMarcPreviewStore } from './marcPreview';

export const useStatusState = () => createSelectors(useStatusStore).use;
export const useLoadingState = () => createSelectors(useLoadingStateStore).use;
export const useMarcPreviewState = () => createSelectors(useMarcPreviewStore).use;
2 changes: 1 addition & 1 deletion src/store/utils/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const updateValue = <V, T>(value: V, updatedValue: T): V => {
if (updatedValue instanceof Map) {
updatedValue.forEach((value, key) => newMap.set(key, value));
} else if (typeof updatedValue === 'object' && updatedValue !== null) {
Object.entries(updatedValue).forEach(([k, v]) => newMap.set(k, v));
Object.entries(updatedValue).forEach(([key, value]) => newMap.set(key, value));
}

return newMap as any;
Expand Down
11 changes: 6 additions & 5 deletions src/test/__tests__/common/hooks/useLoadSearchResults.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { useSearchParams } from 'react-router-dom';
import { SetterOrUpdater, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { SearchQueryParams } from '@common/constants/routes.constants';
import { useLoadSearchResults } from '@common/hooks/useLoadSearchResults';
import { useLoadingStateStore } from '@src/store';
import { setInitialGlobalState } from '@src/test/__mocks__/store';

jest.mock('react-router-dom', () => ({
useSearchParams: jest.fn(),
Expand All @@ -20,9 +22,6 @@ jest.mock('@state', () => ({
index: '',
query: '',
},
loadingState: {
isLoading: false,
},
},
}));

Expand All @@ -33,6 +32,10 @@ describe('useLoadSearchResults', () => {
const setQuery = jest.fn();
const setIsLoading = jest.fn();

beforeEach(() => {
setInitialGlobalState(useLoadingStateStore, { isLoading: false, setIsLoading });
});

it('fetches data and updates state with query and searchBy', async () => {
const searchParams = new URLSearchParams({
[SearchQueryParams.Query]: 'test query',
Expand All @@ -44,7 +47,6 @@ describe('useLoadSearchResults', () => {
(useSetRecoilState as jest.Mock)
.mockReturnValueOnce(setData)
.mockReturnValueOnce(setSearchBy)
.mockReturnValueOnce(setIsLoading)
.mockReturnValueOnce(setQuery);
(useRecoilState as jest.Mock).mockReturnValue([false, (value: boolean) => value] as [
boolean,
Expand All @@ -66,7 +68,6 @@ describe('useLoadSearchResults', () => {
(useSetRecoilState as jest.Mock)
.mockReturnValueOnce(setData)
.mockReturnValueOnce(setSearchBy)
.mockReturnValueOnce(setIsLoading)
.mockReturnValueOnce(setQuery);
(useRecoilState as jest.Mock).mockReturnValue([false, (value: boolean) => value] as [
boolean,
Expand Down
8 changes: 4 additions & 4 deletions src/test/__tests__/common/hooks/useSearch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { useSearchParams } from 'react-router-dom';
import { useRecoilState, useSetRecoilState, useResetRecoilState } from 'recoil';
import { useSearch } from '@common/hooks/useSearch';
import { useSearchContext } from '@common/hooks/useSearchContext';
import { useLoadingStateStore } from '@src/store';
import { setInitialGlobalState } from '@src/test/__mocks__/store';

const selectedNavigationSegment = 'defaultSegment';
const defaultSearchBy = 'defaultSearchBy';
Expand Down Expand Up @@ -50,9 +52,6 @@ jest.mock('@state', () => ({
userValues: {},
previewContent: {},
},
loadingState: {
isLoading: false,
},
},
}));

Expand Down Expand Up @@ -81,6 +80,7 @@ describe('useSearch hook', () => {
};

beforeEach(() => {
setInitialGlobalState(useLoadingStateStore, { isLoading: false, setIsLoading });
(useSearchContext as jest.Mock).mockReturnValue(mockUseSearchContext);
(useSearchParams as jest.Mock).mockReturnValue([null, setSearchParams]);

Expand All @@ -101,7 +101,7 @@ describe('useSearch hook', () => {
},
setFacetsBySegments,
]);
(useSetRecoilState as jest.Mock).mockReturnValueOnce(setIsLoading).mockReturnValueOnce(setForceRefreshSearch);
(useSetRecoilState as jest.Mock).mockReturnValueOnce(setForceRefreshSearch);
(useResetRecoilState as jest.Mock)
.mockReturnValueOnce(resetPreviewContent)
.mockReturnValueOnce(clearFacetsBySegments);
Expand Down
10 changes: 6 additions & 4 deletions src/test/__tests__/views/Root.test.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import '@src/test/__mocks__/common/hooks/useRoutePathPattern.mock';
import '@src/test/__mocks__/components/Loading.mock';
import { useRoutePathPattern } from '@src/test/__mocks__/common/hooks/useRoutePathPattern.mock';
import { render, screen } from '@testing-library/react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { useRoutePathPattern } from '@src/test/__mocks__/common/hooks/useRoutePathPattern.mock';
import { RecoilRoot } from 'recoil';
import { setInitialGlobalState } from '@src/test/__mocks__/store';
import { useLoadingStateStore } from '@src/store';
import { Root } from '@views';
import state from '@state';

jest.mock('@common/constants/build.constants', () => ({ IS_EMBEDDED_MODE: false }));

useRoutePathPattern.mockImplementation(() => null);

describe('Root', () => {
function renderRootComponent(isLoading = false) {
setInitialGlobalState(useLoadingStateStore, { isLoading });

render(
<RecoilRoot initializeState={snapshot => snapshot.set(state.loadingState.isLoading, isLoading)}>
<RecoilRoot>
<BrowserRouter basename="/">
<Routes>
<Route path="/" element={<Root />} />
Expand Down
4 changes: 2 additions & 2 deletions src/views/Edit/Edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { RecordStatus, ResourceType } from '@common/constants/record.constants';
import { EditPreview } from '@components/EditPreview';
import { QueryParams } from '@common/constants/routes.constants';
import { ViewMarcModal } from '@components/ViewMarcModal';
import { useMarcPreviewState, useStatusState } from '@src/store';
import { useLoadingState, useMarcPreviewState, useStatusState } from '@src/store';
import state from '@state';
import './Edit.scss';

Expand All @@ -28,7 +28,7 @@ export const Edit = () => {
const { recordStatus, addStatusMessage } = useStatusState();
const { basicValue: marcPreviewData, resetBasicValue: resetMarcPreviewData } = useMarcPreviewState();
const recordStatusType = recordStatus?.type;
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const { setIsLoading } = useLoadingState();
const setCurrentlyEditedEntityBfid = useSetRecoilState(state.ui.currentlyEditedEntityBfid);
const setCurrentlyPreviewedEntityBfid = useSetRecoilState(state.ui.currentlyPreviewedEntityBfid);
const [queryParams] = useSearchParams();
Expand Down
5 changes: 2 additions & 3 deletions src/views/Root/Root.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Outlet } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import classNames from 'classnames';
import { MODAL_CONTAINER_ID } from '@common/constants/uiElements.constants';
import { FIXED_HEIGHT_VIEWS } from '@common/constants/routes.constants';
Expand All @@ -8,11 +7,11 @@ import { CommonStatus } from '@components/CommonStatus';
import { Nav } from '@components/Nav';
import { Loading } from '@components/Loading';
import { Footer } from '@components/Footer';
import state from '@state';
import { useLoadingState } from '@src/store';

export const Root = () => {
const fixedHeightContainerView = useRoutePathPattern(FIXED_HEIGHT_VIEWS);
const isLoading = useRecoilValue(state.loadingState.isLoading);
const { isLoading } = useLoadingState();

return (
<div data-testid="root" id="app-root">
Expand Down

0 comments on commit 6afd2d6

Please sign in to comment.