Skip to content

Commit da536ed

Browse files
authored
Add useTransaction hook (#134)
* Add `useTransaction` hook boilerplate * Add docs for `useTransaction` hook * Run `yarn changeset` and add minor bump * Return array from `useTransaction` and add `With useContract` story for `useTransaction` * Refactor useTransaction story
1 parent 825561b commit da536ed

File tree

4 files changed

+187
-0
lines changed

4 files changed

+187
-0
lines changed

.changeset/fresh-camels-pay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@web3-ui/hooks': minor
3+
---
4+
5+
Add useTransaction hook

packages/hooks/src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { useWallet } from './useWallet';
22
export { useContract } from './useContract';
3+
export { useTransaction } from './useTransaction';
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
3+
/**
4+
* @dev Hook to get the loading status, error, and data of a function call.
5+
* @param method Function to call
6+
* @param args an array of arguments to pass to the function
7+
* @returns {
8+
* execute: () => Promise<any>,
9+
* loading: boolean,
10+
* error: null | Error,
11+
* } {
12+
* execute: On calling this method, the function is executed with the passed arguments and the loading status is set to true.
13+
* loading: this is true while the function is executing and will be false when the function has finished executing.,
14+
* error: this will be null when there is no error and in case of error, it will contain the error object.
15+
* }
16+
*/
17+
18+
export function useTransaction(method, args: any[] = []) {
19+
const [loading, setLoading] = React.useState<boolean>(false);
20+
const [error, setError] = React.useState<any>(null);
21+
22+
const execute = async () => {
23+
setLoading(true);
24+
setError(null);
25+
try {
26+
const response = await method(...args);
27+
setError(null);
28+
setLoading(false);
29+
return response;
30+
} catch (error) {
31+
setError(error);
32+
setLoading(false);
33+
return error;
34+
}
35+
};
36+
37+
return [execute, loading, error];
38+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { Button, Text, VStack, Input } from '@chakra-ui/react';
2+
import React from 'react';
3+
import { NETWORKS, Provider, useWallet, useContract } from '..';
4+
import { useTransaction } from '../hooks';
5+
6+
export default {
7+
title: 'Hooks/useTransaction',
8+
};
9+
10+
const ADDRESS = '0x7e1D33FcF1C6b6fd301e0B7305dD40E543CF7135'; // Rinkeby
11+
const ABI = [
12+
{
13+
inputs: [
14+
{
15+
internalType: 'string',
16+
name: '_greeting',
17+
type: 'string',
18+
},
19+
],
20+
stateMutability: 'nonpayable',
21+
type: 'constructor',
22+
},
23+
{
24+
inputs: [],
25+
name: 'greet',
26+
outputs: [
27+
{
28+
internalType: 'string',
29+
name: '',
30+
type: 'string',
31+
},
32+
],
33+
stateMutability: 'view',
34+
type: 'function',
35+
},
36+
{
37+
inputs: [
38+
{
39+
internalType: 'string',
40+
name: '_greeting',
41+
type: 'string',
42+
},
43+
],
44+
name: 'setGreeting',
45+
outputs: [],
46+
stateMutability: 'nonpayable',
47+
type: 'function',
48+
},
49+
{
50+
inputs: [
51+
{
52+
internalType: 'address payable',
53+
name: '_to',
54+
type: 'address',
55+
},
56+
],
57+
name: 'transferTo',
58+
outputs: [],
59+
stateMutability: 'payable',
60+
type: 'function',
61+
},
62+
];
63+
64+
const UsingUseContract = () => {
65+
const { connectWallet, disconnectWallet, connected } = useWallet();
66+
const contract = useContract(ADDRESS, ABI);
67+
const [value, setValue] = React.useState('');
68+
69+
const [greet, greetLoading, greetError] = useTransaction(async () =>
70+
// @ts-expect-error
71+
alert(await contract.greet())
72+
);
73+
74+
// @ts-expect-error
75+
const [setGreeting, loading, error] = useTransaction(contract.setGreeting, [value]);
76+
77+
if (connected) {
78+
return (
79+
<VStack>
80+
<Button isLoading={greetLoading} onClick={greet}>
81+
Greet
82+
</Button>
83+
{greetError && <Text color='red'>Error while greeting: {greetError.message}</Text>}
84+
<Button onClick={disconnectWallet}>Disconnect Wallet</Button>
85+
<Text fontSize='lg'>Set Greeting</Text>
86+
<Input isDisabled={loading} value={value} onChange={e => setValue(e.target.value)} />
87+
<Button isLoading={loading} onClick={setGreeting}>
88+
Set Greeting
89+
</Button>
90+
{error && <Text color='red'>Error while setting greeting: {error.message}</Text>}
91+
</VStack>
92+
);
93+
}
94+
95+
return (
96+
<>
97+
<Button onClick={connectWallet}>Connect Wallet</Button>
98+
</>
99+
);
100+
};
101+
102+
export const WithUseContract = () => {
103+
return (
104+
<Provider network={NETWORKS.rinkeby}>
105+
<UsingUseContract />
106+
</Provider>
107+
);
108+
};
109+
110+
const UsingProvider = () => {
111+
const { connectWallet, connected, disconnectWallet } = useWallet();
112+
113+
const [execute, loading, error] = useTransaction(connectWallet);
114+
const [executeDisconnectWallet, disconnectWalletLoading, disconnectWalletError] = useTransaction(
115+
disconnectWallet
116+
);
117+
118+
if (connected) {
119+
return (
120+
<div>
121+
<Button onClick={executeDisconnectWallet}>Disconnect Wallet</Button>
122+
<Text>Loading: {disconnectWalletLoading.toString()}</Text>
123+
<Text color={disconnectWalletError ? 'red' : 'black'}>
124+
Error: {disconnectWalletError ? disconnectWalletError : 'No error'}
125+
</Text>
126+
</div>
127+
);
128+
}
129+
130+
return (
131+
<div>
132+
<Button onClick={execute}>Connect Wallet</Button>
133+
<Text>Loading: {loading.toString()}</Text>
134+
<Text color={error ? 'red' : 'black'}>Error: {error ? error : 'No error'}</Text>
135+
</div>
136+
);
137+
};
138+
139+
export const ConnectWallet = () => (
140+
<Provider network={NETWORKS.mainnet}>
141+
<UsingProvider />
142+
</Provider>
143+
);

0 commit comments

Comments
 (0)