From 3ccc5e0251ff3f4f5f0da5950a038d8d1f21e70c Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Fri, 15 Dec 2023 01:48:17 -0500 Subject: [PATCH] feat: throw error if useWallet is being used outside the WalletProvider (#125) * chore: import zustand/shallow from named export This resolves the deprecated warning that started appearing in the console after upgrading to zustand@^4.3.8 * feat: useWallet throws error if used outside WalletProvider This also renames the context provider to match the exported "WalletProvider", and the context object itself is imported via a `useWalletContext` hook. --- src/context/WalletContext.tsx | 21 +++++++++++++++++++++ src/hooks/useWallet.test.tsx | 23 +++++++++++++++-------- src/hooks/useWallet.ts | 8 ++++---- src/index.ts | 2 +- src/store/index.ts | 1 - src/store/state/clientStore.ts | 8 -------- src/testUtils/createWrapper.tsx | 16 ---------------- 7 files changed, 41 insertions(+), 38 deletions(-) create mode 100644 src/context/WalletContext.tsx delete mode 100644 src/store/state/clientStore.ts delete mode 100644 src/testUtils/createWrapper.tsx diff --git a/src/context/WalletContext.tsx b/src/context/WalletContext.tsx new file mode 100644 index 00000000..4b1d0c69 --- /dev/null +++ b/src/context/WalletContext.tsx @@ -0,0 +1,21 @@ +import React, { createContext, useContext } from 'react' +import type { SupportedProviders } from 'src/types/providers' + +const WalletContext = createContext(null) + +export const useWalletContext = (): SupportedProviders | null => { + const context = useContext(WalletContext) + if (context === undefined) { + throw new Error('useWallet must be used within the WalletProvider') + } + return context +} + +export interface WalletProviderProps { + children: React.ReactNode + value: SupportedProviders | null +} + +export const WalletProvider = ({ children, value }: WalletProviderProps) => { + return {children} +} diff --git a/src/hooks/useWallet.test.tsx b/src/hooks/useWallet.test.tsx index a904313a..6b8e0920 100644 --- a/src/hooks/useWallet.test.tsx +++ b/src/hooks/useWallet.test.tsx @@ -7,9 +7,8 @@ import React from 'react' import useWallet from './useWallet' import DeflyWalletClient from '../clients/defly' import PeraWalletClient from '../clients/pera' -import { default as ClientProvider } from '../store/state/clientStore' +import { WalletProvider, type WalletProviderProps } from '../context/WalletContext' import { useHydratedWalletStore } from '../store/state/walletStore' -import { createWrapper } from '../testUtils/createWrapper' import { mockAccounts } from '../testUtils/mockAccounts' import { createDeflyMockInstance, createPeraMockInstance } from '../testUtils/mockClients' import { clearAccounts } from '../utils/clearAccounts' @@ -36,7 +35,7 @@ jest.mock('../store/state/walletStore', () => ({ })) jest.mock('../', () => ({ - ClientProvider: ({ children }: { children: React.ReactNode }) =>
{children}
+ WalletProvider: ({ children }: WalletProviderProps) =>
{children}
})) jest.mock('../utils/clearAccounts') @@ -59,7 +58,7 @@ describe('useWallet', () => { jest.spyOn(peraMockInstance, 'disconnect') jest.spyOn(peraMockInstance, 'getAccountInfo') - // Passed to `ClientProvider` in renderHook wrapper + // Passed to `WalletProvider` in renderHook wrapper mockClientProviders = { pera: peraMockInstance, defly: deflyMockInstance @@ -72,7 +71,9 @@ describe('useWallet', () => { it('should return active and connected accounts', () => { const { result } = renderHook(() => useWallet(), { - wrapper: createWrapper(ClientProvider, { value: mockClientProviders }) + wrapper: ({ children }) => ( + {children} + ) }) // Active account @@ -87,7 +88,9 @@ describe('useWallet', () => { it('should return `providers` array', async () => { const { result } = renderHook(() => useWallet(), { - wrapper: createWrapper(ClientProvider, { value: mockClientProviders }) + wrapper: ({ children }) => ( + {children} + ) }) const providers = result.current.providers @@ -134,7 +137,9 @@ describe('useWallet', () => { it('should return status flags', () => { const { result } = renderHook(() => useWallet(), { - wrapper: createWrapper(ClientProvider, { value: mockClientProviders }) + wrapper: ({ children }) => ( + {children} + ) }) expect(result.current.isActive).toBe(true) @@ -143,7 +148,9 @@ describe('useWallet', () => { it('should return `getAccountInfo`', async () => { const { result } = renderHook(() => useWallet(), { - wrapper: createWrapper(ClientProvider, { value: mockClientProviders }) + wrapper: ({ children }) => ( + {children} + ) }) await act(async () => { diff --git a/src/hooks/useWallet.ts b/src/hooks/useWallet.ts index 87665dbc..1cbd918b 100644 --- a/src/hooks/useWallet.ts +++ b/src/hooks/useWallet.ts @@ -1,17 +1,17 @@ -import { useState, useMemo, useContext, useEffect } from 'react' +import { useState, useMemo, useEffect } from 'react' import type algosdk from 'algosdk' import { getAlgosdk } from '../algod' import { useHydratedWalletStore, walletStoreSelector } from '../store/index' import { TransactionsArray, WalletClient, Provider } from '../types' import { PROVIDER_ID } from '../constants' -import { ClientContext } from '../store/state/clientStore' +import { useWalletContext } from '../context/WalletContext' import allClients from '../clients' import { clearAccounts } from '../utils/clearAccounts' -import shallow from 'zustand/shallow' +import { shallow } from 'zustand/shallow' export default function useWallet() { const [providers, setProviders] = useState(null) - const clients = useContext(ClientContext) + const clients = useWalletContext() const { activeAccount, diff --git a/src/index.ts b/src/index.ts index 337a7dcb..9ce97a1e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ export { reconnectProviders, encodeNFDTransactionsArray } from './utils' -export { WalletProvider } from './store' +export { WalletProvider } from './context/WalletContext' export * from './constants' export * from './types' export * from './clients' diff --git a/src/store/index.ts b/src/store/index.ts index 8114091f..5c125abd 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,3 +1,2 @@ export * from './state/walletStore' export * from './state/debugStore' -export { default as WalletProvider } from './state/clientStore' diff --git a/src/store/state/clientStore.ts b/src/store/state/clientStore.ts deleted file mode 100644 index fade7191..00000000 --- a/src/store/state/clientStore.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createContext } from 'react' -import { SupportedProviders } from '../../types/providers' - -const ClientContext = createContext(null) - -export { ClientContext } - -export default ClientContext.Provider diff --git a/src/testUtils/createWrapper.tsx b/src/testUtils/createWrapper.tsx deleted file mode 100644 index aefee13e..00000000 --- a/src/testUtils/createWrapper.tsx +++ /dev/null @@ -1,16 +0,0 @@ -// See https://testing-library.com/docs/react-testing-library/api#renderhook-options - -import React, { ComponentType, ReactNode } from 'react' - -interface CreatedWrapperProps { - children: ReactNode -} - -export function createWrapper

( - Wrapper: ComponentType

, - props: P -): React.FC { - return function CreatedWrapper({ children }: CreatedWrapperProps) { - return {children} - } -}