Skip to content

Commit 160fa13

Browse files
authored
Merge pull request #32 from EDOHWARES/feat/react-hooks
feat(react): create React hooks packaging layer (@bc-forge/react)
2 parents 80c9c6a + c10f3a8 commit 160fa13

5 files changed

Lines changed: 168 additions & 0 deletions

File tree

react/package.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "@bc-forge/react",
3+
"version": "1.0.0",
4+
"description": "React hooks packaging layer for bc-forge SDK",
5+
"main": "dist/index.js",
6+
"module": "dist/index.mjs",
7+
"types": "dist/index.d.ts",
8+
"files": [
9+
"dist"
10+
],
11+
"scripts": {
12+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
13+
"dev": "tsup src/index.ts --format cjs,esm --watch --dts",
14+
"lint": "eslint src/**/*.ts"
15+
},
16+
"peerDependencies": {
17+
"react": "^18.0.0 || ^19.0.0",
18+
"react-dom": "^18.0.0 || ^19.0.0"
19+
},
20+
"dependencies": {
21+
"@bc-forge/sdk": "file:../sdk"
22+
},
23+
"devDependencies": {
24+
"@types/react": "^18.0.0",
25+
"@types/react-dom": "^18.0.0",
26+
"react": "^18.0.0",
27+
"react-dom": "^18.0.0",
28+
"tsup": "^8.0.0",
29+
"typescript": "^5.0.0"
30+
}
31+
}

react/src/context.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React, { createContext, useContext, useMemo, ReactNode } from 'react';
2+
import { bcForgeClient, bcForgeClientConfig } from '@bc-forge/sdk';
3+
4+
interface bcForgeContextType {
5+
client: bcForgeClient | null;
6+
}
7+
8+
const bcForgeContext = createContext<bcForgeContextType>({ client: null });
9+
10+
export interface bcForgeProviderProps {
11+
config: bcForgeClientConfig;
12+
children: ReactNode;
13+
}
14+
15+
export const bcForgeProvider: React.FC<bcForgeProviderProps> = ({ config, children }) => {
16+
const client = useMemo(() => new bcForgeClient(config), [config.rpcUrl, config.networkPassphrase, config.contractId]);
17+
18+
return (
19+
<bcForgeContext.Provider value={{ client }}>
20+
{children}
21+
</bcForgeContext.Provider>
22+
);
23+
};
24+
25+
export const useBcForgeClient = () => {
26+
const context = useContext(bcForgeContext);
27+
if (!context.client) {
28+
throw new Error('useBcForgeClient must be used within a bcForgeProvider');
29+
}
30+
return context.client;
31+
};

react/src/hooks.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { useState, useEffect, useCallback } from 'react';
2+
import { useBcForgeClient } from './context';
3+
import { Keypair } from '@stellar/stellar-sdk';
4+
5+
/**
6+
* Hook to fetch basic token information (name, symbol, decimals).
7+
*/
8+
export function useBcForgeToken() {
9+
const client = useBcForgeClient();
10+
const [data, setData] = useState<{ name: string; symbol: string; decimals: number } | null>(null);
11+
const [loading, setLoading] = useState(true);
12+
const [error, setError] = useState<Error | null>(null);
13+
14+
useEffect(() => {
15+
async function fetchData() {
16+
try {
17+
setLoading(true);
18+
const [name, symbol, decimals] = await Promise.all([
19+
client.getName(),
20+
client.getSymbol(),
21+
client.getDecimals(),
22+
]);
23+
setData({ name, symbol, decimals });
24+
} catch (err) {
25+
setError(err instanceof Error ? err : new Error(String(err)));
26+
} finally {
27+
setLoading(false);
28+
}
29+
}
30+
fetchData();
31+
}, [client]);
32+
33+
return { data, loading, error };
34+
}
35+
36+
/**
37+
* Hook to fetch the balance of a specific address.
38+
*/
39+
export function useBalance(address: string | undefined) {
40+
const client = useBcForgeClient();
41+
const [data, setData] = useState<bigint | null>(null);
42+
const [loading, setLoading] = useState(false);
43+
const [error, setError] = useState<Error | null>(null);
44+
45+
const fetchBalance = useCallback(async () => {
46+
if (!address) return;
47+
try {
48+
setLoading(true);
49+
const balance = await client.getBalance(address);
50+
setData(balance);
51+
} catch (err) {
52+
setError(err instanceof Error ? err : new Error(String(err)));
53+
} finally {
54+
setLoading(false);
55+
}
56+
}, [client, address]);
57+
58+
useEffect(() => {
59+
fetchBalance();
60+
}, [fetchBalance]);
61+
62+
return { data, loading, error, refetch: fetchBalance };
63+
}
64+
65+
/**
66+
* Hook to perform mint operations.
67+
*/
68+
export function useMint() {
69+
const client = useBcForgeClient();
70+
const [loading, setLoading] = useState(false);
71+
const [error, setError] = useState<Error | null>(null);
72+
73+
const mint = useCallback(async (to: string, amount: bigint, source: Keypair) => {
74+
try {
75+
setLoading(true);
76+
setError(null);
77+
const result = await client.mint(to, amount, source);
78+
return result;
79+
} catch (err) {
80+
const error = err instanceof Error ? err : new Error(String(err));
81+
setError(error);
82+
throw error;
83+
} finally {
84+
setLoading(false);
85+
}
86+
}, [client]);
87+
88+
return { mint, loading, error };
89+
}

react/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './context';
2+
export * from './hooks';

react/tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"module": "ESNext",
5+
"moduleResolution": "node",
6+
"jsx": "react-jsx",
7+
"strict": true,
8+
"esModuleInterop": true,
9+
"skipLibCheck": true,
10+
"forceConsistentCasingInFileNames": true,
11+
"declaration": true,
12+
"outDir": "./dist"
13+
},
14+
"include": ["src"]
15+
}

0 commit comments

Comments
 (0)