Hooks
No hooks found in any category.
useProxy
stateInstallation
npx usehooks-cli@latest add use-proxy
Description
A React hook that creates and manages JavaScript Proxy objects, enabling you to intercept and customize operations performed on objects (such as property lookup, assignment, enumeration, function invocation, etc.). Perfect for creating reactive objects, validation, logging, or data transformation.
Parameters
Name | Type | Default | Description |
---|---|---|---|
initialTarget | T extends object | - | The initial target object to be proxied |
handler | ProxyHandler<T> | - | An object that defines which operations will be intercepted and how to redefine intercepted operations |
Return Type
UseProxyReturn<T>
Property | Type | Description |
---|---|---|
proxy | T | The proxy object that intercepts operations on the target |
target | T | The original target object |
updateTarget | (newTarget: T | ((prevTarget: T) => T)) => void | Function to update the target object |
revoke | () => void | Function to revoke the proxy, making it unusable |
isRevoked | boolean | Whether the proxy has been revoked |
Examples
Property Access Logging
Log all property access on an object
1const { proxy } = useProxy(
2 { name: 'John', age: 30 },
3 {
4 get(target, prop) {
5 console.log(`Accessing property: ${String(prop)}`);
6 return target[prop];
7 }
8 }
9);
10
11// Usage: proxy.name will log "Accessing property: name"
Property Validation
Validate property assignments
1const { proxy } = useProxy(
2 { age: 0 },
3 {
4 set(target, prop, value) {
5 if (prop === 'age' && (typeof value !== 'number' || value < 0)) {
6 throw new Error('Age must be a positive number');
7 }
8 target[prop] = value;
9 return true;
10 }
11 }
12);
13
14// proxy.age = -5; // Throws error
Default Values
Provide default values for undefined properties
1const { proxy } = useProxy(
2 {},
3 {
4 get(target, prop) {
5 return prop in target ? target[prop] : 'default';
6 }
7 }
8);
9
10// proxy.anyProperty returns 'default' if not set
Revocable Proxy
Create a proxy that can be revoked
1const { proxy, revoke, isRevoked } = useProxy(
2 { data: 'sensitive' },
3 {
4 get(target, prop) {
5 console.log('Access granted');
6 return target[prop];
7 }
8 }
9);
10
11// Later revoke access
12revoke();
13console.log(isRevoked); // true
Dependencies
react
Notes
- •The proxy is recreated when the target or handler changes
- •Once revoked, the proxy becomes unusable and operations will throw TypeError
- •The hook uses Proxy.revocable() to allow controlled revocation
- •All handler methods are optional - omitted traps forward to the target
- •Perfect for implementing reactive objects, validation, or debugging
Implementation
1"use client";
2
3import { useState, useCallback, useMemo } from "react";
4
5type ProxyHandler<T extends object> = {
6 get?: (target: T, property: string | symbol, receiver: any) => any;
7 set?: (
8 target: T,
9 property: string | symbol,
10 value: any,
11 receiver: any
12 ) => boolean;
13 has?: (target: T, property: string | symbol) => boolean;
14 deleteProperty?: (target: T, property: string | symbol) => boolean;
15 ownKeys?: (target: T) => ArrayLike<string | symbol>;
16 getOwnPropertyDescriptor?: (
17 target: T,
18 property: string | symbol
19 ) => PropertyDescriptor | undefined;
20 defineProperty?: (
21 target: T,
22 property: string | symbol,
23 attributes: PropertyDescriptor
24 ) => boolean;
25 preventExtensions?: (target: T) => boolean;
26 getPrototypeOf?: (target: T) => object | null;
27 isExtensible?: (target: T) => boolean;
28 setPrototypeOf?: (target: T, prototype: object | null) => boolean;
29 apply?: (target: T, thisArg: any, argArray: any[]) => any;
30 construct?: (target: T, argArray: any[], newTarget: Function) => object;
31};
32
33interface UseProxyReturn<T extends object> {
34 proxy: T;
35 target: T;
36 updateTarget: (newTarget: T | ((prevTarget: T) => T)) => void;
37 revoke: () => void;
38 isRevoked: boolean;
39}
40
41export function useProxy<T extends object>(
42 initialTarget: T,
43 handler: ProxyHandler<T>
44): UseProxyReturn<T> {
45 const [target, setTarget] = useState<T>(initialTarget);
46 const [isRevoked, setIsRevoked] = useState(false);
47
48 // Create a revocable proxy
49 const { proxy, revoke: revokeProxy } = useMemo(() => {
50 if (isRevoked) {
51 return { proxy: target, revoke: () => {} };
52 }
53 return Proxy.revocable(target, handler);
54 }, [target, handler, isRevoked]);
55
56 const updateTarget = useCallback(
57 (newTarget: T | ((prevTarget: T) => T)) => {
58 if (isRevoked) {
59 console.warn("Cannot update target of a revoked proxy");
60 return;
61 }
62 setTarget((prevTarget) =>
63 typeof newTarget === "function"
64 ? (newTarget as (prevTarget: T) => T)(prevTarget)
65 : newTarget
66 );
67 },
68 [isRevoked]
69 );
70
71 const revoke = useCallback(() => {
72 if (!isRevoked) {
73 revokeProxy();
74 setIsRevoked(true);
75 }
76 }, [revokeProxy, isRevoked]);
77
78 return {
79 proxy: isRevoked ? target : proxy,
80 target,
81 updateTarget,
82 revoke,
83 isRevoked,
84 };
85}
86