-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A
useWallets()
hook that only vends handles (#53)
This is the first React hook in the new Wallet Standard React package. ```ts function MyComponent() { const bitcoinWallets = useWallets().filter(({chains}) => chains.some(chain => chain.startsWith('bitcoin:')), ); return ( <ul> {bitcoinWallets.map( ({name}) => <li key={name}>{name}</li> )} </ul> ); } ``` Characteristcs: * Causes a rerender when a wallet is registered, unregistered, or changed * If a wallet (or wallet account within) hasn't changed since last render it will be referentially equal to the last observed value
- Loading branch information
Showing
10 changed files
with
114 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@wallet-standard/react-core': major | ||
--- | ||
|
||
A `useWallets()` hook you can use to obtain an array of `UiWallet` objects that represent the currently registered Wallet Standard wallets. You can render these wallets in the UI of your application using the `name` and `icon` properties within, you can enumerate the `UiWalletAccount` objects authorized for the current domain through the `accounts` property, and you can use the `UiWallet` itself with compatible hooks, to materialize wallet features and more. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# `@wallet-standard/react-core` | ||
|
||
This package provides React hooks for using Wallet Standard wallets, accounts, and features. | ||
|
||
## Hooks | ||
|
||
### `useWallets()` | ||
|
||
Vends an array of `UiWallet` objects; one for every registered Wallet Standard `Wallet`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
export * from '@wallet-standard/ui'; | ||
|
||
export * from './features/index.js'; | ||
|
||
export * from './useWallet.js'; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,17 @@ | ||
import { getWallets } from '@wallet-standard/app'; | ||
import type { Wallet, WalletWithFeatures } from '@wallet-standard/base'; | ||
import { StandardEvents, type StandardEventsFeature } from '@wallet-standard/features'; | ||
import { useCallback, useRef, useSyncExternalStore } from 'react'; | ||
import type { UiWallet } from '@wallet-standard/ui'; | ||
import { getOrCreateUiWalletForStandardWallet_DO_NOT_USE_OR_YOU_WILL_BE_FIRED } from '@wallet-standard/ui-registry'; | ||
import { useMemo } from 'react'; | ||
|
||
import { hasEventsFeature } from './WalletProvider.js'; | ||
import { useStable } from './useStable.js'; | ||
import { useWallets_INTERNAL_ONLY_NOT_FOR_EXPORT } from './useWallets_INTERNAL_ONLY_NOT_FOR_EXPORT.js'; | ||
|
||
const NO_WALLETS: readonly Wallet[] = []; | ||
|
||
function getServerSnapshot(): readonly Wallet[] { | ||
return NO_WALLETS; | ||
} | ||
|
||
function walletHasStandardEventsFeature(wallet: Wallet): wallet is WalletWithFeatures<StandardEventsFeature> { | ||
return hasEventsFeature(wallet.features); | ||
} | ||
|
||
/** TODO: docs */ | ||
export function useWallets(): readonly Wallet[] { | ||
const { get, on } = useStable(getWallets); | ||
const prevWallets = useRef(get()); | ||
const outputWallets = useRef(prevWallets.current); | ||
const getSnapshot = useCallback(() => { | ||
const nextWallets = get(); | ||
if (nextWallets !== prevWallets.current) { | ||
// The Wallet Standard itself recyled the wallets array wrapper. Use that array. | ||
outputWallets.current = nextWallets; | ||
} | ||
prevWallets.current = nextWallets; | ||
return outputWallets.current; | ||
}, [get]); | ||
const subscribe = useCallback( | ||
(onStoreChange: () => void) => { | ||
const disposeRegisterListener = on('register', onStoreChange); | ||
const disposeUnregisterListener = on('unregister', onStoreChange); | ||
const disposeWalletChangeListeners = get() | ||
.filter(walletHasStandardEventsFeature) | ||
.map((wallet) => | ||
wallet.features[StandardEvents].on('change', () => { | ||
// Despite a change in a property of a wallet, the array that contains the | ||
// list of wallets will be reused. The wallets array before and after the | ||
// change will be referentially equal. | ||
// | ||
// Here, we force a new wallets array wrapper to be created by cloning the | ||
// array. This gives React the signal to re-render, because it will notice | ||
// that the return value of `getSnapshot()` has changed. | ||
outputWallets.current = [...get()]; | ||
onStoreChange(); | ||
}) | ||
); | ||
return () => { | ||
disposeRegisterListener(); | ||
disposeUnregisterListener(); | ||
disposeWalletChangeListeners.forEach((d) => d()); | ||
}; | ||
}, | ||
[get, on] | ||
/** | ||
* Vends an array of `UiWallet` objects; one for every registered Wallet Standard `Wallet`. | ||
*/ | ||
export function useWallets(): readonly UiWallet[] { | ||
const wallets = useWallets_INTERNAL_ONLY_NOT_FOR_EXPORT(); | ||
const uiWallets = useMemo( | ||
() => wallets.map(getOrCreateUiWalletForStandardWallet_DO_NOT_USE_OR_YOU_WILL_BE_FIRED), | ||
[wallets] | ||
); | ||
return useSyncExternalStore<readonly Wallet[]>(subscribe, getSnapshot, getServerSnapshot); | ||
return uiWallets; | ||
} |
61 changes: 61 additions & 0 deletions
61
packages/react/core/src/useWallets_INTERNAL_ONLY_NOT_FOR_EXPORT.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { getWallets } from '@wallet-standard/app'; | ||
import type { Wallet, WalletWithFeatures } from '@wallet-standard/base'; | ||
import { StandardEvents, type StandardEventsFeature } from '@wallet-standard/features'; | ||
import { useCallback, useRef, useSyncExternalStore } from 'react'; | ||
|
||
import { hasEventsFeature } from './WalletProvider.js'; | ||
import { useStable } from './useStable.js'; | ||
|
||
const NO_WALLETS: readonly Wallet[] = []; | ||
|
||
function getServerSnapshot(): readonly Wallet[] { | ||
return NO_WALLETS; | ||
} | ||
|
||
function walletHasStandardEventsFeature(wallet: Wallet): wallet is WalletWithFeatures<StandardEventsFeature> { | ||
return hasEventsFeature(wallet.features); | ||
} | ||
|
||
/** TODO: docs */ | ||
export function useWallets_INTERNAL_ONLY_NOT_FOR_EXPORT(): readonly Wallet[] { | ||
const { get, on } = useStable(getWallets); | ||
const prevWallets = useRef(get()); | ||
const outputWallets = useRef(prevWallets.current); | ||
const getSnapshot = useCallback(() => { | ||
const nextWallets = get(); | ||
if (nextWallets !== prevWallets.current) { | ||
// The Wallet Standard itself recyled the wallets array wrapper. Use that array. | ||
outputWallets.current = nextWallets; | ||
} | ||
prevWallets.current = nextWallets; | ||
return outputWallets.current; | ||
}, [get]); | ||
const subscribe = useCallback( | ||
(onStoreChange: () => void) => { | ||
const disposeRegisterListener = on('register', onStoreChange); | ||
const disposeUnregisterListener = on('unregister', onStoreChange); | ||
const disposeWalletChangeListeners = get() | ||
.filter(walletHasStandardEventsFeature) | ||
.map((wallet) => | ||
wallet.features[StandardEvents].on('change', () => { | ||
// Despite a change in a property of a wallet, the array that contains the | ||
// list of wallets will be reused. The wallets array before and after the | ||
// change will be referentially equal. | ||
// | ||
// Here, we force a new wallets array wrapper to be created by cloning the | ||
// array. This gives React the signal to re-render, because it will notice | ||
// that the return value of `getSnapshot()` has changed. | ||
outputWallets.current = [...get()]; | ||
onStoreChange(); | ||
}) | ||
); | ||
return () => { | ||
disposeRegisterListener(); | ||
disposeUnregisterListener(); | ||
disposeWalletChangeListeners.forEach((d) => d()); | ||
}; | ||
}, | ||
[get, on] | ||
); | ||
return useSyncExternalStore<readonly Wallet[]>(subscribe, getSnapshot, getServerSnapshot); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.