Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(React) Allow Hooks to Be Invoked Without Passing an Account #116

Closed
alex-bubblemaps opened this issue Feb 12, 2025 · 4 comments
Closed
Labels
enhancement New feature or request question Add this to close an issue with instructions on how to repost as a question on Stack Exchange

Comments

@alex-bubblemaps
Copy link

alex-bubblemaps commented Feb 12, 2025

Motivation

Allowing developers to invoke hooks (e.g., useWalletAccountMessageSigner) without requiring an account provides greater flexibility in where these hooks are called. This removes the need to check for an account before rendering a component that depends on the hook. Additionally, it enables the use of these hooks in higher-level components and contexts.

Example Use Case

In a cross-chain application, it can be useful to define an abstract interface with common methods and properties (e.g., address, isConnected, signMessage). To achieve this, we could wrap the application in a BlockchainContext to determine which implementation of the abstract interface to invoke.

For example, if Solana is the active blockchain, we could have a something like this:

Abstract interface:

export interface Web3InteractorContext {
  address: string | null;
  isConnected: boolean;
  connectors: Web3InteractorConnectors;
  disconnect: () => Promise<void>;
  signMessage: (message: string) => Promise<string>;
}

export interface Web3InteractorConnector {
  name: string;
  icon: string;
  connect: () => Promise<void>;
}

type Web3InteractorConnectors = Web3InteractorConnector[];

Solana context implementation:

const SolanaWeb3InteractorContext = createContext<Web3InteractorContext | null>(
  null
);

export default SolanaWeb3InteractorContext;

Solana context provider (using v1):

export default function SolanaWeb3InteractorProvider({
  children
}: SolanaWeb3InteractorProviderProps) {
  const { publicKey, connected, disconnect } = useWallet();
  const { handleSignMessage } = useHandleSignMessage();
  const connectors = useConnectors();

  return (
    <SolanaWeb3InteractorContext.Provider
      value={{
        address: publicKey?.toBase58() || null,
        connectors,
        isConnected: connected,
        disconnect,
        signMessage: handleSignMessage
      }}
    >
      {children}
    </SolanaWeb3InteractorContext.Provider>
  );
}

function useHandleSignMessage() {
  const { signMessage } = useWallet(); // NO NEED TO PASS AN ACCOUNT

  const handleSignMessage = async (message: string) => {
    if (!signMessage) {
      throw new Error('Sign message function not available');
    }

    const encodedMessage = new TextEncoder().encode(message);
    const signature = await signMessage(encodedMessage);

    return bs58.encode(signature);
  };

  return { handleSignMessage };
}

However, in version 2 (v2), this approach is no longer possible. For example, we can’t provide a method to handle signatures since the new React v2 hook requires an account as an argument.

Details

I’m not sure how these hooks work internally, but a potential solution could be to return undefined when no account is provided.

@alex-bubblemaps alex-bubblemaps added the enhancement New feature or request label Feb 12, 2025
@3girls1cup

This comment has been minimized.

@steveluscher
Copy link
Collaborator

steveluscher commented Feb 16, 2025

This is a good React component design question, and I'd be happy to continue it if you'd be interested in posting it to sola.na/sse.

In the meantime, take a look at how we handle a wallet failing to implement certain features or chains in the example React UI

<FeaturePanel label="Sign Message">
<ErrorBoundary
FallbackComponent={FeatureNotSupportedCallout}
resetKeys={errorBoundaryResetKeys}
>
<SolanaSignMessageFeaturePanel account={selectedWalletAccount} />
</ErrorBoundary>

@steveluscher steveluscher added the question Add this to close an issue with instructions on how to repost as a question on Stack Exchange label Feb 16, 2025
Copy link

Hi @alex-bubblemaps,

Thanks for your question!

We want to make sure to keep signal strong in the GitHub issue tracker – to make sure that it remains the best place to track issues that affect the development of the Solana JavaScript SDK itself.

Questions like yours deserve a purpose-built Q&A forum. Unless there exists evidence that this is a bug with the Solana JavaScript SDK itself, please post your question to the Solana Stack Exchange using this link: https://solana.stackexchange.com/questions/ask


This automated message is a result of having added the ‘question’ tag.

@alex-bubblemaps
Copy link
Author

alex-bubblemaps commented Feb 17, 2025

Thanks a lot, @steveluscher, for your time and response! I'll post my question on Solana Stack Exchange.

About handling an undefined account in hooks—I’m not too comfortable using an ErrorBoundary for this. In my view, a user who hasn’t connected their wallet yet isn’t an error; it’s just a normal scenario. I’d rather keep the boundary for actual errors.

Additionally, my goal is to provide common Web3 properties and methods (like address and signMessage) through a higher-level context. That means I’ll need to call the hook handling signatures in the same place where I retrieve the current address—which could be undefined.

To be honest, I haven’t explored this in depth yet. My question was more about the argument passed to the hook, which could be an issue since we can’t conditionally call a hook. 🙂

Link to the discussion

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Add this to close an issue with instructions on how to repost as a question on Stack Exchange
Projects
None yet
Development

No branches or pull requests

3 participants