Skip to content
This repository has been archived by the owner on Aug 16, 2024. It is now read-only.

Commit

Permalink
useConnect: Make connect function undefined until connector is ready (#…
Browse files Browse the repository at this point in the history
…60)

This prevents the `connect` function from being called before
`WalletConnectionProps.activeConnector` is ready as the call would then
fail. The expectation is that the button/function invoking `connect`
checks that doing so is valid. With this change that check is now more
direct and less error-prone.

This a only barely a breaking change because the expected usage of
passing the function to `onClick` of a button keeps working without
modification. The difference is that now the click is a noop if the call
is invaild instead of an exception being thrown. And checking validity
is just the truthiness value of `connect`. In other words you can now
just do
```tsx
<Button type="button" onClick={connect} disabled={!connect}>Connect</Button>
```
Nice and simple.

An alternative solution would be to return an additional boolean
`isReadyToConnect`, but that would not allow the type system to prevent
the call like it does with this one.
  • Loading branch information
bisgardo authored Mar 13, 2024
1 parent a012bf8 commit b5ebf2b
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 20 deletions.
11 changes: 11 additions & 0 deletions packages/react-components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- `useConnect` (breaking): Let `connection` function be `undefined` if `connector` is.
This prevents the function from being called before `WalletConnectionProps.activeConnector` is ready which would fail anyway.
The expectation before was that the button/function invoking `connect` would check this itself,
but making it explicit in the type seems less prone to errors.
To migrate, replace `connect()` with `connect && connect()` or `connect?.()`
and optionally replace any guarding using `activeConnector` by the truthiness value of `connect` itself.

## [0.5.0] - 2024-03-13

### Changed

- Dependency on `@concordium/wallet-connectors` bumped to v0.5.0+.

## [0.4.0] - 2023-11-13
Expand Down
9 changes: 9 additions & 0 deletions packages/react-components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ const { connect, isConnecting, connectError } = useConnect(activeConnector, setC

The app uses the function `connect` to initiate a new connection from `activeConnector`.
The fields `isConnecting` and `connectError` are used to render the connection status.
If `activeConnector` is `undefined` then so is `connect` as it doesn't make sense to call it in that case.
This may be used to disable a button whose click handler invokes the function, like for instance:

```tsx
<Button type="button" onClick={connect} disabled={!connect}>
Connect
</Button>
```

Once established, the connection and its state are exposed in the following fields:

- `connection`: The `WalletConnection` object that the app uses to interact with the wallet.
Expand Down
40 changes: 23 additions & 17 deletions packages/react-components/src/useConnect.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useState } from 'react';
import { useMemo, useState } from 'react';
import { WalletConnection, WalletConnector } from '@concordium/wallet-connectors';
import { errorString } from './error';

Expand All @@ -7,9 +7,10 @@ import { errorString } from './error';
*/
export interface Connect {
/**
* Function for initiating a new connection. Any existing connection will not be automatically disconnected.
* Function for initiating a new connection using the provided connector or undefined if none was provided.
* Any existing connection will not be automatically disconnected by calling the function.
*/
connect: () => void;
connect: (() => void) | undefined;

/**
* Indicator on whether we're waiting for a connection to be established and approved.
Expand All @@ -25,7 +26,7 @@ export interface Connect {
/**
* Hook that exposes a function for initiating a connection on the provided {@link connector} and,
* if successful, store the resulting connection in {@link setConnection}.
* The hook also exposes the status of the connection progress and error if initiation failed.
* The hook also exposes the status of the connection progress and an error if initiation failed.
* @param connector The connector from which new connections are to be initiated.
* @param setConnection The setter function to which new connections are passed.
*/
Expand All @@ -35,21 +36,26 @@ export function useConnect(
): Connect {
const [isConnecting, setIsConnecting] = useState(false);
const [connectError, setConnectError] = useState('');
const connect = useCallback(() => {
const connect = useMemo(() => {
if (!connector) {
throw new Error('no connector to connect from');
// There's no connector to connect to.
// Previously this would result in an exception being thrown by the returned function.
// Now we return 'undefined' instead to indicate that it isn't ready to be called.
return undefined;
}
setIsConnecting(true);
connector
.connect()
.then((c) => {
if (c) {
setConnection(c);
setConnectError('');
}
})
.catch((e) => setConnectError(errorString(e)))
.finally(() => setIsConnecting(false));
return () => {
setIsConnecting(true);
connector
.connect()
.then((c) => {
if (c) {
setConnection(c);
setConnectError('');
}
})
.catch((e) => setConnectError(errorString(e)))
.finally(() => setIsConnecting(false));
};
}, [connector, setConnection]);
return { connect, isConnecting, connectError };
}
2 changes: 1 addition & 1 deletion samples/contractupdate/src/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function Main(props: WalletConnectionProps) {
{activeConnectorError && <Alert variant="danger">Connector error: {activeConnectorError}.</Alert>}
{!activeConnectorError && activeConnectorType && !activeConnector && <Spinner />}
{connectError && <Alert variant="danger">Connection error: {connectError}.</Alert>}
{activeConnector && !account && (
{connect && !account && (
<Button type="button" onClick={connect} disabled={isConnecting}>
{isConnecting && 'Connecting...'}
{!isConnecting && activeConnectorType === BROWSER_WALLET && 'Connect Browser Wallet'}
Expand Down
2 changes: 1 addition & 1 deletion samples/proofs/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function Main(props: WalletConnectionProps) {
{activeConnectorError && <Alert variant="danger">Connector error: {activeConnectorError}.</Alert>}
{!activeConnectorError && activeConnectorType && !activeConnector && <Spinner />}
{connectError && <Alert variant="danger">Connection error: {connectError}.</Alert>}
{activeConnector && !account && (
{connect && !account && (
<Button type="button" onClick={connect} disabled={isConnecting}>
{isConnecting && 'Connecting...'}
{!isConnecting && activeConnectorType === BROWSER_WALLET && 'Connect Browser Wallet'}
Expand Down
2 changes: 1 addition & 1 deletion samples/sign-message/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ function Main(props: WalletConnectionProps) {
{activeConnectorError && <Alert variant="danger">Connector error: {activeConnectorError}.</Alert>}
{!activeConnectorError && activeConnectorType && !activeConnector && <Spinner />}
{connectError && <Alert variant="danger">Connection error: {connectError}.</Alert>}
{activeConnector && !account && (
{connect && !account && (
<Button type="button" onClick={connect} disabled={isConnecting}>
{isConnecting && 'Connecting...'}
{!isConnecting && activeConnectorType === BROWSER_WALLET && 'Connect Browser Wallet'}
Expand Down

0 comments on commit b5ebf2b

Please sign in to comment.