React hooks error when using Solana Integration

42 views Asked by At

I am not a frontend developer but currently trying to construct a test frontend for Solana app using the available Solana Dapp Scaffold. However, I have the following error:

"Account gathering failed! Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem."

let PROGRAM_ABI; // Define PROGRAM_ABI variable
    
// Fetch the JSON file using fetch API
fetch('../deployment_data/xxx.json')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json(); // Parse the JSON response
  })
  .then(data => {
    // Assign the parsed JSON data to PROGRAM_ABI
    PROGRAM_ABI = data;
    console.log('PROGRAM_ABI:', PROGRAM_ABI);
  })
  .catch(error => {
    console.error('Error fetching JSON:', error);
  });
export const GetContractAccounts: FC = () => {
  const { connection } = useConnection();
  const { publicKey } = useWallet();
  const { getUserSOLBalance } = useUserSOLBalanceStore();

  const onClick = useCallback(async () => {
    if (!publicKey) {
      console.log('error', 'Wallet not connected!');
      notify({
        type: 'error',
        message: 'error',
        description: 'Wallet not connected!'
      });
      return;
    }

    let signature: TransactionSignature = '';

    try {
      const secret = [my private key]; // Replace with your secret
           
      const fromKeypair = Keypair.fromSecretKey(new Uint8Array(secret));

      const communityId = 123;
      const communityName = "MyCommunity";
      const parentCommunityId = 456;
      const tags = [];
      const chainID = 789;
      const chainName = "Solana";

      const dataArray = [
        communityId.toString(),
        communityName.toString(),
        parentCommunityId.toString(),
        tags.join(','),
        chainID.toString(),
        chainName.toString()
      ];
      const concatenatedData = dataArray.join('');

      const { publicKey, signTransaction, signAllTransactions } = useWallet();
      const signerWallet = {
        publicKey: publicKey,
        signTransaction: signTransaction,
        signAllTransactions: signAllTransactions,
      };
      const provider = new anchor.Provider(
        connection,
        signerWallet,
        { commitment: "processed" }
      );

      const votingProgram = new PublicKey('my public key');
      const program = new anchor.Program(
        PROGRAM_ABI,
        votingProgram,
        provider
      );
      const accounts = await program
        .account
        .votingProgram
        .fetch(fromKeypair.publicKey)
      console.log(accounts)
    } catch (error: any) {
      notify({
        type: 'error',
        message: `Account gathering failed!`,
        description: error?.message,
        txid: signature
      });
      console.log(
        'error',
        `Account gathering failed! ${error?.message}`,
        signature
      );
    }
  }, [publicKey, connection]);

  return (
    <div className="flex flex-row justify-center">
      <div className="relative group items-center">
        <div
          className="m-1 absolute -inset-0.5 bg-gradient-to-r 
            from-indigo-500 to-fuchsia-500 rounded-lg blur opacity-20 
            group-hover:opacity-100 transition duration-1000
            group-hover:duration-200 animate-tilt"
        ></div>
        <button
          className="px-8 m-2 btn animate-pulse bg-gradient-to-br
            from-indigo-500 to-fuchsia-500 hover:from-white
            hover:to-purple-300 text-black"
          onClick={onClick}
        >
          <span>Contract Accounts</span>
        </button>
      </div>
    </div>
  );
};
1

There are 1 answers

0
Drew Reese On

The onClick callback calls useWallet which breaks one of React's Rules of Hooks. You should not call React hooks in nested functions/callbacks/etc.

Use the useWallet in the outer scope to access the publicKey, signTransaction, and signAllTransactions values, ensuring these are properly added to the useCallback hook's dependency array.

export const GetContractAccounts: FC = () => {
  const { connection } = useConnection();
  const {
    publicKey,
    signTransaction,
    signAllTransactions
  } = useWallet();

  ...

  const onClick = useCallback(async () => {
    if (!publicKey) {
      ...
    }

    ...

    try {
      ...

      const signerWallet = {
        publicKey,
        signTransaction,
        signAllTransactions,
      };

      const provider = new anchor.Provider(
        connection,
        signerWallet,
        { commitment: "processed" }
      );

      ...
    } catch (error: any) {
      ...
    }
  }, [publicKey, signTransaction, signAllTransactions, connection]);

  return (
    <div className="flex flex-row justify-center">
      <div className="relative group items-center">
        <div className="....." />
        <button className="...." onClick={onClick}>
          <span>Contract Accounts</span>
        </button>
      </div>
    </div>
  );
};