refactor: Add custom hooks for context access in various contexts
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// src/contexts/AuthContext.tsx
|
||||
import React, { createContext, useState, useEffect, useCallback, ReactNode } from 'react';
|
||||
import React, { createContext, useState, useEffect, useCallback, ReactNode, useContext } from 'react';
|
||||
import type { User, UserProfile } from '../types';
|
||||
import * as apiClient from '../services/apiClient';
|
||||
import { useApi } from '../hooks/useApi';
|
||||
@@ -120,4 +120,17 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
const value = { user, profile, authStatus, isLoading, login, logout, updateProfile };
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom hook to access the authentication context.
|
||||
* This is what components will use to get auth state and methods.
|
||||
* It also ensures that it's used within an AuthProvider.
|
||||
*/
|
||||
export const useAuth = (): AuthContextType => {
|
||||
const context = useContext(AuthContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useAuth must be used within an AuthProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/contexts/FlyersContext.tsx
|
||||
import React, { createContext, ReactNode } from 'react';
|
||||
import React, { createContext, ReactNode, useContext } from 'react';
|
||||
import type { Flyer } from '../types';
|
||||
import * as apiClient from '../services/apiClient';
|
||||
import { useInfiniteQuery } from '../hooks/useInfiniteQuery';
|
||||
@@ -38,4 +38,12 @@ export const FlyersProvider: React.FC<{ children: ReactNode }> = ({ children })
|
||||
};
|
||||
|
||||
return <FlyersContext.Provider value={value}>{children}</FlyersContext.Provider>;
|
||||
};
|
||||
|
||||
export const useFlyers = (): FlyersContextType => {
|
||||
const context = useContext(FlyersContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useFlyers must be used within a FlyersProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/contexts/MasterItemsContext.tsx
|
||||
import React, { createContext, ReactNode, useMemo } from 'react';
|
||||
import React, { createContext, ReactNode, useMemo, useContext } from 'react';
|
||||
import type { MasterGroceryItem } from '../types';
|
||||
import * as apiClient from '../services/apiClient';
|
||||
import { useApiOnMount } from '../hooks/useApiOnMount';
|
||||
@@ -24,4 +24,12 @@ export const MasterItemsProvider: React.FC<{ children: ReactNode }> = ({ childre
|
||||
}), [data, loading, error]);
|
||||
|
||||
return <MasterItemsContext.Provider value={value}>{children}</MasterItemsContext.Provider>;
|
||||
};
|
||||
|
||||
export const useMasterItems = (): MasterItemsContextType => {
|
||||
const context = useContext(MasterItemsContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useMasterItems must be used within a MasterItemsProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/contexts/ModalContext.tsx
|
||||
import React, { createContext, useState, useMemo, useCallback } from 'react';
|
||||
import React, { createContext, useState, useMemo, useCallback, useContext } from 'react';
|
||||
|
||||
/**
|
||||
* Defines the names of all modals used in the application.
|
||||
@@ -40,4 +40,16 @@ export const ModalProvider: React.FC<{ children: React.ReactNode }> = ({ childre
|
||||
const value = useMemo(() => ({ openModal, closeModal, isModalOpen }), [openModal, closeModal, isModalOpen]);
|
||||
|
||||
return <ModalContext.Provider value={value}>{children}</ModalContext.Provider>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The custom hook that components will use to access the modal context.
|
||||
* It provides a clean and simple API for interacting with modals.
|
||||
*/
|
||||
export const useModal = (): ModalContextType => {
|
||||
const context = useContext(ModalContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useModal must be used within a ModalProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
// src/contexts/UserDataContext.tsx
|
||||
import React, { createContext, useState, useEffect, useMemo, ReactNode } from 'react';
|
||||
import React, { createContext, useState, useEffect, useMemo, ReactNode, useContext } from 'react';
|
||||
import type { MasterGroceryItem, ShoppingList } from '../types';
|
||||
import * as apiClient from '../services/apiClient';
|
||||
import { useApiOnMount } from '../hooks/useApiOnMount';
|
||||
import { useAuth } from '../hooks/useAuth';
|
||||
import { useAuth } from './AuthContext';
|
||||
|
||||
export interface UserDataContextType {
|
||||
watchedItems: MasterGroceryItem[];
|
||||
@@ -54,4 +54,12 @@ export const UserDataProvider: React.FC<{ children: ReactNode }> = ({ children }
|
||||
}), [watchedItems, shoppingLists, user, isLoadingWatched, isLoadingShoppingLists, watchedItemsError, shoppingListsError]);
|
||||
|
||||
return <UserDataContext.Provider value={value}>{children}</UserDataContext.Provider>;
|
||||
};
|
||||
|
||||
export const useUserData = (): UserDataContextType => {
|
||||
const context = useContext(UserDataContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useUserData must be used within a UserDataProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
0
src/contexts/useAuth.tsx
Normal file
0
src/contexts/useAuth.tsx
Normal file
0
src/contexts/useFlyers.tsx
Normal file
0
src/contexts/useFlyers.tsx
Normal file
0
src/contexts/useMasterItems.tsx
Normal file
0
src/contexts/useMasterItems.tsx
Normal file
0
src/contexts/useModal.tsx
Normal file
0
src/contexts/useModal.tsx
Normal file
0
src/contexts/useUserData.tsx
Normal file
0
src/contexts/useUserData.tsx
Normal file
@@ -2,8 +2,8 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { useActiveDeals } from './useActiveDeals';
|
||||
import * as apiClient from '../services/apiClient';
|
||||
import { useFlyers } from './useFlyers';
|
||||
import * as apiClient from '../services/apiClient'; // Correct path
|
||||
import { useFlyers } from '../contexts/FlyersContext';
|
||||
import { useUserData } from './useUserData';
|
||||
import type { Flyer, MasterGroceryItem, FlyerItem, DealItem } from '../types';
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { Flyer, MasterGroceryItem, FlyerItem, DealItem } from '../types';
|
||||
const mockedApiClient = vi.mocked(apiClient);
|
||||
|
||||
// Mock the new data provider hooks
|
||||
vi.mock('./useFlyers');
|
||||
vi.mock('../contexts/FlyersContext');
|
||||
vi.mock('./useUserData');
|
||||
|
||||
// Create typed mocks for easier usage
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// src/hooks/useActiveDeals.tsx
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useFlyers } from './useFlyers';
|
||||
import { useFlyers } from '../contexts/FlyersContext';
|
||||
import { useUserData } from './useUserData';
|
||||
import { useApi } from './useApi';
|
||||
import type { FlyerItem, DealItem } from '../types';
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { useAuth } from './useAuth';
|
||||
import { AuthProvider } from '../contexts/AuthContext';
|
||||
import { AuthProvider, useAuth } from '../contexts/AuthContext';
|
||||
import * as apiClient from '../services/apiClient';
|
||||
import type { User, UserProfile } from '../types';
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useFlyers } from './useFlyers';
|
||||
import { FlyersProvider } from '../contexts/FlyersContext';
|
||||
import { FlyersProvider, useFlyers } from '../contexts/FlyersContext';
|
||||
import { useInfiniteQuery } from './useInfiniteQuery';
|
||||
import type { Flyer } from '../types';
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useMasterItems } from './useMasterItems';
|
||||
import { MasterItemsProvider } from '../contexts/MasterItemsContext';
|
||||
import { MasterItemsProvider, useMasterItems } from '../contexts/MasterItemsContext';
|
||||
import { useApiOnMount } from './useApiOnMount';
|
||||
import type { MasterGroceryItem } from '../types';
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import React from 'react';
|
||||
import { useModal } from './useModal';
|
||||
import { ModalProvider } from '../contexts/ModalContext';
|
||||
import { ModalProvider, useModal } from '../contexts/ModalContext';
|
||||
|
||||
// Create a wrapper component that includes the ModalProvider.
|
||||
// This is necessary because the useModal hook depends on the context provided by ModalProvider.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// src/hooks/useShoppingLists.tsx
|
||||
import { useState, useCallback, useEffect, useMemo } from 'react';
|
||||
import { useAuth } from './useAuth';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useUserData } from './useUserData';
|
||||
import { useApi } from './useApi';
|
||||
import * as apiClient from '../services/apiClient';
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useUserData } from './useUserData';
|
||||
import { UserDataProvider } from '../contexts/UserDataContext';
|
||||
import { useAuth } from './useAuth';
|
||||
import { UserDataProvider, useUserData } from '../contexts/UserDataContext';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useApiOnMount } from './useApiOnMount';
|
||||
import type { MasterGroceryItem, ShoppingList, UserProfile } from '../types';
|
||||
|
||||
// 1. Mock the hook's dependencies
|
||||
vi.mock('./useAuth');
|
||||
vi.mock('../contexts/AuthContext');
|
||||
vi.mock('./useApiOnMount');
|
||||
|
||||
// 2. Create typed mocks for type safety and autocompletion
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useWatchedItems } from './useWatchedItems';
|
||||
import { useApi } from './useApi';
|
||||
import { useAuth } from './useAuth';
|
||||
import { useApi } from '../hooks/useApi';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useUserData } from './useUserData';
|
||||
import type { MasterGroceryItem, User } from '../types';
|
||||
|
||||
// Mock the hooks that useWatchedItems depends on
|
||||
vi.mock('./useApi');
|
||||
vi.mock('./useAuth');
|
||||
vi.mock('../hooks/useApi');
|
||||
vi.mock('../contexts/AuthContext');
|
||||
vi.mock('./useUserData');
|
||||
|
||||
// The apiClient is globally mocked in our test setup, so we just need to cast it
|
||||
@@ -35,8 +35,8 @@ describe('useWatchedItems Hook', () => {
|
||||
|
||||
// Provide a default implementation for useApi
|
||||
mockedUseApi
|
||||
.mockReturnValueOnce({ execute: mockAddWatchedItemApi, error: null, data: null, loading: false, isRefetching: false })
|
||||
.mockReturnValueOnce({ execute: mockRemoveWatchedItemApi, error: null, data: null, loading: false, isRefetching: false });
|
||||
.mockReturnValueOnce({ execute: mockAddWatchedItemApi, error: null, data: null, loading: false, isRefetching: false, reset: vi.fn() })
|
||||
.mockReturnValueOnce({ execute: mockRemoveWatchedItemApi, error: null, data: null, loading: false, isRefetching: false, reset: vi.fn() });
|
||||
|
||||
|
||||
// Provide a default implementation for the mocked hooks
|
||||
@@ -98,6 +98,7 @@ describe('useWatchedItems Hook', () => {
|
||||
data: null,
|
||||
loading: false,
|
||||
isRefetching: false,
|
||||
reset: vi.fn(),
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useWatchedItems());
|
||||
@@ -134,8 +135,8 @@ describe('useWatchedItems Hook', () => {
|
||||
|
||||
it('should set an error message if the API call fails', async () => {
|
||||
// Re-mock useApi for this specific error test case
|
||||
mockedUseApi.mockReturnValueOnce({ execute: vi.fn(), error: null, data: null, loading: false, isRefetching: false }) // for add
|
||||
.mockReturnValueOnce({ execute: vi.fn(), error: new Error('Deletion Failed'), data: null, loading: false, isRefetching: false }); // for remove
|
||||
mockedUseApi.mockReturnValueOnce({ execute: vi.fn(), error: null, data: null, loading: false, isRefetching: false, reset: vi.fn() }) // for add
|
||||
.mockReturnValueOnce({ execute: vi.fn(), error: new Error('Deletion Failed'), data: null, loading: false, isRefetching: false, reset: vi.fn() }); // for remove
|
||||
|
||||
const { result } = renderHook(() => useWatchedItems());
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// src/hooks/useWatchedItems.tsx
|
||||
import { useMemo, useCallback } from 'react';
|
||||
import { useAuth } from './useAuth';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useApi } from './useApi';
|
||||
import { useUserData } from './useUserData';
|
||||
import * as apiClient from '../services/apiClient';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user