javascriptreactjseventsreact-hookswagmi

Wagmi useContractEvent listener can't access useState variable


I'm stuck on this problem that I really can't make sense of.

When trying to access the hash state variable from the listener function in useContractEvent it is undefined.

I can access the hash state variable outside of the listener function in useContractEvent but as soon as the event is triggered and I try to read the hash state variable inside the listener function it is undefined.

Is it a context issue?

const [hash, setHash] = useState<`0x${string}` | undefined>();

useContractEvent({
  address: contract.address as `0x${string}`,
  abi: contract.abi,
  eventName: "SomeEvent",
  listener(log) {
    console.log(hash) // undefined
    if (log[0]?.transactionHash != hash) return;
    // never reached
  },
});

const { write } = useContractWrite({
  address: contract.address as `0x${string}`,
  abi: contract.abi,
  functionName: "someFunction",
  args: [],
  onSuccess(data) {
    setHash(data.hash);
  },
});

return (
 <button onClick={() => write?.()}>
    Go
  </button>
);

Solution

  • Yes, the issue you're facing is likely due to a context problem related to closures and the asynchronous nature of useContractEvent and useContractWrite.

    One approach is to use ref to always access the latest value of hash

    const hashRef = useRef<`0x${string}` | undefined>();
    
    useContractEvent({
        address: contract.address as `0x${string}`,
        abi: contract.abi,
        eventName: "SomeEvent",
        listener: (log) => {
            console.log(hashRef.current); // This will always log the latest value of `hash`
            if (log[0]?.transactionHash !== hashRef.current) return;
            // never reached
        },
    });
    
    const { write } = useContractWrite({
        address: contract.address as `0x${string}`,
        abi: contract.abi,
        functionName: "someFunction",
        args: [],
        onSuccess(data) {
            hashRef.current = data.hash; 
        },
    });
    
    return <button onClick={() => write?.()}>Go</button>;