This document describes all custom React hooks provided by PropChain Frontend. Each hook is documented with its parameters, return values, and usage examples.
File: src/hooks/useTransaction.ts
Manages blockchain transactions within the application. Provides functions to queue, retry, and cancel transactions. Requires a connected wallet.
| Property | Type | Description |
|---|---|---|
addTransactionToQueue |
(params: TransactionParams) => void |
Adds a new transaction to the monitoring queue |
retryTransaction |
(tx: Transaction) => void |
Attempts to retry a failed transaction |
cancelTransaction |
(id: string) => void |
Attempts to cancel a pending transaction |
| Property | Type | Required | Description |
|---|---|---|---|
hash |
string |
✅ | Transaction hash returned by the wallet |
type |
TransactionType |
✅ | Category of transaction (e.g. 'purchase', 'transfer') |
to |
string |
— | Recipient address |
value |
string |
— | Wei value as a decimal string |
data |
string |
— | Encoded calldata (hex) |
description |
string |
— | Human-readable description shown in the UI |
propertyId |
string |
— | Associated property ID for real-estate transactions |
requiredConfirmations |
number |
— | Minimum block confirmations before marking complete (default: 1) |
import { useTransaction } from '@/hooks/useTransaction';
function PurchaseButton({ propertyId }: { propertyId: string }) {
const { addTransactionToQueue } = useTransaction();
const handlePurchase = async () => {
const hash = await wallet.sendTransaction({ ... });
addTransactionToQueue({
hash,
type: 'purchase',
propertyId,
description: 'Property token purchase',
requiredConfirmations: 2,
});
};
return <button onClick={handlePurchase}>Buy</button>;
}File: src/hooks/useSecurity.ts
Provides comprehensive Web3 security checks including wallet validation, transaction risk assessment, signature verification, and rate limiting. Integrates with blockchainSecurity, PhishingProtection, WalletValidator, and auditLogger.
| Property | Type | Description |
|---|---|---|
securityState |
SecurityState |
Current security status and rate-limit counters |
validateWalletConnection |
(address, walletType, chainId) => Promise<ValidationResult> |
Validates a wallet before connecting |
validateTransaction |
(to, value, data) => Promise<TransactionValidation> |
Validates a transaction before signing |
validateSignature |
(message, signature?) => Promise<SignatureResult> |
Validates a signature request |
monitorTransaction |
(hash, to, value, gasUsed?, gasPrice?) => void |
Records a completed transaction for anomaly detection |
getRiskAssessment |
() => RiskAssessment | null |
Returns the current wallet's risk metrics |
handleWalletDisconnection |
() => void |
Logs wallet disconnection to the audit trail |
handleNetworkSwitch |
(fromChainId, toChainId) => void |
Logs network switches to the audit trail |
updateSecurityState |
() => void |
Manually refreshes the security state |
interface SecurityState {
isSecure: boolean; // false if riskScore >= 50 or critical alerts exist
riskScore: number; // 0–100 composite risk score
warnings: string[]; // Non-blocking risk factors
alerts: string[]; // Recent security alert messages
rateLimitStatus: {
walletConnections: { allowed: boolean; remaining: number; retryAfter?: number };
transactions: { allowed: boolean; remaining: number; retryAfter?: number };
signatures: { allowed: boolean; remaining: number; retryAfter?: number };
};
lastSecurityCheck: number; // Unix timestamp of last check
}import { useSecurity } from '@/hooks/useSecurity';
function TransactionForm() {
const { validateTransaction, securityState } = useSecurity();
const handleSubmit = async (to: string, value: string) => {
const result = await validateTransaction(to, value, '0x');
if (!result.isValid) {
console.error('Blocked:', result.blocks);
return;
}
if (result.requiresConfirmation) {
// Show confirmation dialog with result.warnings
}
// proceed with transaction
};
return <div>Risk score: {securityState.riskScore}</div>;
}File: src/hooks/usePropertySearchQuery.ts (React Query implementation)
Combines the Zustand search store, React Query caching, and URL synchronization into a single hook. Reads filters/sort/page from the store, delegates fetching to usePropertySearchQuery which uses useQuery under the hood for automatic caching, deduplication, and stale-while-revalidate behaviour.
| Property | Type | Description |
|---|---|---|
filters |
SearchFilters |
Current active filter values |
sortBy |
SortOption |
Current sort selection |
page |
number |
Current page number (1-indexed) |
resultsPerPage |
number |
Number of results per page |
properties |
Property[] |
Current page of search results |
totalResults |
number |
Total matching properties across all pages |
totalPages |
number |
Computed total page count |
isLoading |
boolean |
true while a React Query fetch is pending or refetching |
error |
string | null |
Error message from the last failed fetch |
lastUpdated |
Date | undefined |
Date of the last successful React Query fetch |
setFilters |
(filters: SearchFilters) => void |
Replace all filters at once |
setFilter |
(key, value) => void |
Update a single filter key |
clearFilters |
() => void |
Reset all filters to defaults |
setSortBy |
(sort: SortOption) => void |
Change the sort order |
setPage |
(page: number) => void |
Navigate to a page (also scrolls to top) |
setResultsPerPage |
(count: number) => void |
Change the number of results per page |
loadMore |
() => void |
Append the next page without scrolling to top |
refetch |
() => Promise<void> |
Manually trigger a fresh React Query refetch |
- Empty results:
propertiesis[],totalResultsis0,totalPagesis0. - Fetch error:
errorreceives the message;propertiesis cleared to[]. - Rapid filter changes: React Query deduplicates concurrent requests for the same query key.
- Stale data: Cached results are served instantly for 5 minutes (
staleTime); a background refetch updates silently. - Window refocus: Does NOT trigger a refetch (
refetchOnWindowFocus: false). - 4xx errors: Not retried. Network/5xx errors retried up to 3 times.
- URL sync: The parent component is responsible for URL parameter synchronization (see
src/hooks/usePropertySearch.ts).
import { usePropertySearch } from '@/hooks/usePropertySearch';
function PropertyList() {
const { properties, isLoading, error, setFilter, page, totalPages, setPage } =
usePropertySearch();
if (isLoading) return <Spinner />;
if (error) return <ErrorMessage message={error} />;
return (
<>
<input
placeholder="Min price"
onChange={(e) => setFilter('minPrice', Number(e.target.value))}
/>
{properties.map((p) => <PropertyCard key={p.id} property={p} />)}
<Pagination current={page} total={totalPages} onChange={setPage} />
</>
);
}File: src/hooks/useDebounce.ts
Delays updating a value until a specified time has elapsed since the last change. Useful for reducing API calls triggered by fast-changing inputs such as search fields.
| Parameter | Type | Default | Description |
|---|---|---|---|
value |
T |
— | The value to debounce |
delay |
number |
500 |
Debounce delay in milliseconds |
T — The debounced value, updated only after delay ms of inactivity.
import { useDebounce } from '@/hooks/useDebounce';
import { usePropertySearch } from '@/hooks/usePropertySearch';
function SearchInput() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
const { setFilter } = usePropertySearch();
// Only triggers a search 300ms after the user stops typing
useEffect(() => {
setFilter('query', debouncedQuery);
}, [debouncedQuery]);
return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
}File: src/hooks/useDeviceOrientation.ts
Subscribes to the browser's DeviceOrientationEvent API to provide real-time device tilt data. Handles the iOS 13+ permission model automatically.
| Property | Type | Description |
|---|---|---|
orientation |
DeviceOrientationData |
Current orientation angles |
isSupported |
boolean |
true if DeviceOrientationEvent is available |
hasPermission |
boolean |
true if the user has granted permission (or permission is not required) |
requestPermission |
() => Promise<boolean> |
Prompts the user for permission (required on iOS 13+) |
error |
string | null |
Error message if orientation is unavailable or permission was denied |
| Property | Type | Description |
|---|---|---|
alpha |
number | null |
Rotation around Z-axis (compass heading), 0–360° |
beta |
number | null |
Rotation around X-axis (front-to-back tilt), −180° to 180° |
gamma |
number | null |
Rotation around Y-axis (left-to-right tilt), −90° to 90° |
absolute |
boolean |
true if angles are relative to Earth's coordinate frame |
import { useDeviceOrientation } from '@/hooks/useDeviceOrientation';
function TiltIndicator() {
const { orientation, isSupported, hasPermission, requestPermission, error } =
useDeviceOrientation();
if (!isSupported) return <p>Orientation not supported</p>;
if (error) return <p>Error: {error}</p>;
if (!hasPermission) {
return <button onClick={requestPermission}>Enable tilt detection</button>;
}
return (
<p>
α: {orientation.alpha?.toFixed(1)}°,
β: {orientation.beta?.toFixed(1)}°,
γ: {orientation.gamma?.toFixed(1)}°
</p>
);
}File: src/hooks/useGestures.ts
Attaches touch gesture listeners to a DOM element via a ref. Detects swipes (four directions), pinch-to-zoom, double-tap, and long-press.
| Handler | Type | Description |
|---|---|---|
onSwipeLeft |
() => void |
Fired when a left swipe exceeds the threshold |
onSwipeRight |
() => void |
Fired when a right swipe exceeds the threshold |
onSwipeUp |
() => void |
Fired when an upward swipe exceeds the threshold |
onSwipeDown |
() => void |
Fired when a downward swipe exceeds the threshold |
onPinch |
(scale: number) => void |
Fired continuously during a two-finger pinch; scale is relative to the initial finger distance |
onDoubleTap |
() => void |
Fired when two taps occur within doubleTapDelay ms |
onLongPress |
() => void |
Fired when a touch is held for longPressDelay ms without moving |
| Option | Type | Default | Description |
|---|---|---|---|
threshold |
number |
50 |
Minimum pixel distance to register a swipe |
longPressDelay |
number |
500 |
Milliseconds before a held touch fires onLongPress |
doubleTapDelay |
number |
300 |
Maximum milliseconds between taps to count as a double-tap |
React.RefObject<HTMLElement> — Attach this ref to the element you want to detect gestures on.
import { useGestures } from '@/hooks/useGestures';
function SwipeableCard() {
const ref = useGestures(
{
onSwipeLeft: () => console.log('next card'),
onSwipeRight: () => console.log('previous card'),
onDoubleTap: () => console.log('liked!'),
onLongPress: () => console.log('show options'),
},
{ threshold: 60, longPressDelay: 600 }
);
return (
<div ref={ref as React.RefObject<HTMLDivElement>} className="card">
Swipe me
</div>
);
}