tryError Documentation
React Hooks
Complete guide to using tryError hooks in React applications.
The @tryError/react package provides hooks that integrate seamlessly with React's state management and lifecycle, making error handling declarative and type-safe.
useTry
Async Operation HookHandle async operations with loading states and error handling
Basic Usage
useTry basic exampletypescript
import { useTry } from '@try-error/react';
function UserProfile({ userId }: { userId: string }) {
const { data, error, loading, execute, reset } = useTry(
async () => {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('Failed to fetch user');
return response.json();
},
[userId] // Dependencies - will re-execute when userId changes
);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!data) return <div>No user found</div>;
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
<button onClick={reset}>Refresh</button>
</div>
);
}
API Reference
useTry type signaturetypescript
function useTry<T>(
operation: () => Promise<T>,
deps?: React.DependencyList,
options?: UseTryOptions
): UseTryResult<T>
interface UseTryOptions {
immediate?: boolean; // Execute immediately on mount (default: true)
resetOnDepsChange?: boolean; // Reset state when deps change (default: true)
retryCount?: number; // Number of automatic retries (default: 0)
retryDelay?: number; // Delay between retries in ms (default: 1000)
}
interface UseTryResult<T> {
data: T | null; // Success result
error: TryError | null; // Error result
loading: boolean; // Loading state
execute: () => Promise<void>; // Manual execution function
reset: () => void; // Reset state function
}
Advanced Configuration
useTry with optionstypescript
function DataFetcher() {
const { data, error, loading, execute } = useTry(
async () => {
const result = await tryAsync(() => fetchCriticalData());
if (isTryError(result)) {
throw result; // Re-throw to trigger retry logic
}
return result;
},
[], // No dependencies - manual execution only
{
immediate: false, // Don't execute on mount
retryCount: 3, // Retry up to 3 times
retryDelay: 2000, // Wait 2 seconds between retries
}
);
return (
<div>
<button onClick={execute} disabled={loading}>
{loading ? 'Fetching...' : 'Fetch Data'}
</button>
{error && <ErrorDisplay error={error} />}
{data && <DataDisplay data={data} />}
</div>
);
}
useTryCallback
Event Handler HookCreate error-safe event handlers with loading states
Basic Usage
useTryCallback exampletypescript
import { useTryCallback } from '@try-error/react';
function CreateUserForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const { execute: createUser, loading, error } = useTryCallback(
async (formData: { name: string; email: string }) => {
const result = await tryAsync(() =>
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
})
);
if (isTryError(result)) {
throw result;
}
if (!result.ok) {
throw createError({
type: 'ValidationError',
message: 'Failed to create user',
context: { status: result.status }
});
}
return result.json();
},
{
onSuccess: (user) => {
console.log('User created:', user);
setName('');
setEmail('');
},
onError: (error) => {
console.error('Failed to create user:', error);
}
}
);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
createUser({ name, email });
};
return (
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
disabled={loading}
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
disabled={loading}
/>
<button type="submit" disabled={loading}>
{loading ? 'Creating...' : 'Create User'}
</button>
{error && <div className="error">{error.message}</div>}
</form>
);
}
API Reference
useTryCallback type signaturetypescript
function useTryCallback<TArgs extends any[], TResult>(
callback: (...args: TArgs) => Promise<TResult>,
options?: UseTryCallbackOptions<TResult>
): UseTryCallbackResult<TArgs>
interface UseTryCallbackOptions<T> {
onSuccess?: (result: T) => void;
onError?: (error: TryError) => void;
onSettled?: () => void;
}
interface UseTryCallbackResult<TArgs extends any[]> {
execute: (...args: TArgs) => Promise<void>;
loading: boolean;
error: TryError | null;
reset: () => void;
}
useTryState
State Management HookManage async state with built-in error handling
Basic Usage
useTryState exampletypescript
import { useTryState } from '@try-error/react';
interface User {
id: string;
name: string;
email: string;
}
function UserManager() {
const [users, setUsers, { loading, error, reset }] = useTryState<User[]>([]);
const loadUsers = async () => {
const result = await tryAsync(() => fetch('/api/users').then(r => r.json()));
setUsers(result); // Automatically handles success/error states
};
const addUser = async (newUser: Omit<User, 'id'>) => {
const result = await tryAsync(async () => {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newUser)
});
return response.json();
});
if (isOk(result)) {
setUsers(prev => isOk(prev) ? [...prev, result] : [result]);
}
};
useEffect(() => {
loadUsers();
}, []);
if (loading) return <div>Loading users...</div>;
if (error) return <ErrorDisplay error={error} onRetry={reset} />;
if (!isOk(users)) return <div>No users</div>;
return (
<div>
<UserList users={users} />
<AddUserForm onAdd={addUser} />
</div>
);
}
API Reference
useTryState type signaturetypescript
function useTryState<T>(
initialValue: T
): [
TryResult<T>,
(value: TryResult<T> | ((prev: TryResult<T>) => TryResult<T>)) => void,
{
loading: boolean;
error: TryError | null;
reset: () => void;
}
]
useTryMutation
Mutation HookHandle mutations with optimistic updates and rollback
Optimistic Updates
useTryMutation with optimistic updatestypescript
import { useTryMutation } from '@try-error/react';
function TodoList({ todos, setTodos }: {
todos: Todo[],
setTodos: (todos: Todo[]) => void
}) {
const { mutate: toggleTodo, loading } = useTryMutation({
mutationFn: async (todoId: string) => {
const result = await tryAsync(() =>
fetch(`/api/todos/${todoId}/toggle`, { method: 'PUT' })
);
if (isTryError(result)) throw result;
return result.json();
},
onMutate: async (todoId) => {
// Optimistic update
const previousTodos = todos;
setTodos(todos.map(todo =>
todo.id === todoId
? { ...todo, completed: !todo.completed }
: todo
));
return { previousTodos };
},
onError: (error, todoId, context) => {
// Rollback on error
if (context?.previousTodos) {
setTodos(context.previousTodos);
}
},
onSuccess: (data, todoId) => {
// Sync with server response
setTodos(prev => prev.map(todo =>
todo.id === todoId ? data : todo
));
}
});
return (
<div>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={() => toggleTodo(todo.id)}
disabled={loading}
/>
))}
</div>
);
}
useCleanup
Memory Management HookUniversal cleanup hook for proper memory management in React
Basic Usage
useCleanup basic exampletypescript
import { useCleanup } from '@try-error/react';
function DataFetcher() {
const { isMounted, addCleanup, createAbortController } = useCleanup();
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const controller = createAbortController();
async function fetchData() {
try {
const response = await fetch('/api/data', {
signal: controller.signal
});
if (isMounted()) {
setData(await response.json());
}
} catch (err) {
if (isMounted() && !controller.signal.aborted) {
setError(err);
}
}
}
fetchData();
// Register additional cleanup
addCleanup(() => {
console.log('Component cleanup executed');
});
}, [isMounted, addCleanup, createAbortController]);
return (
<div>
{error && <div>Error: {error.message}</div>}
{data && <div>Data: {JSON.stringify(data)}</div>}
</div>
);
}
Advanced Usage with Ref Management
useCleanup with ref nullificationtypescript
import { useCleanup } from '@try-error/react';
function VideoPlayer({ videoUrl }: { videoUrl: string }) {
const { isMounted, addCleanup, createAbortController, nullifyRef } = useCleanup();
const videoRef = useRef<HTMLVideoElement>(null);
const streamRef = useRef<MediaStream | null>(null);
useEffect(() => {
const controller = createAbortController();
async function setupVideo() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
streamRef.current = stream;
if (isMounted() && videoRef.current) {
videoRef.current.srcObject = stream;
}
// Register cleanup for the stream
addCleanup(() => {
if (streamRef.current) {
streamRef.current.getTracks().forEach(track => track.stop());
}
});
// Register ref for nullification
nullifyRef(streamRef);
} catch (err) {
if (isMounted()) {
console.error('Failed to setup video:', err);
}
}
}
setupVideo();
}, [videoUrl, isMounted, addCleanup, createAbortController, nullifyRef]);
return <video ref={videoRef} autoPlay muted />;
}
Custom Hook Integration
useCleanup in custom hookstypescript
import { useCleanup } from '@try-error/react';
function useWebSocket(url: string) {
const { isMounted, addCleanup, createAbortController } = useCleanup();
const [socket, setSocket] = useState<WebSocket | null>(null);
const [messages, setMessages] = useState<string[]>([]);
useEffect(() => {
const ws = new WebSocket(url);
ws.onopen = () => {
if (isMounted()) {
setSocket(ws);
}
};
ws.onmessage = (event) => {
if (isMounted()) {
setMessages(prev => [...prev, event.data]);
}
};
ws.onclose = () => {
if (isMounted()) {
setSocket(null);
}
};
// Register cleanup for WebSocket
addCleanup(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.close();
}
});
return () => {
ws.close();
};
}, [url, isMounted, addCleanup]);
const sendMessage = useCallback((message: string) => {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(message);
}
}, [socket]);
return { socket, messages, sendMessage };
}
API Reference
useCleanup type signaturetypescript
function useCleanup(): {
// Core functions
isMounted: () => boolean;
addCleanup: (cleanup: () => void) => void;
createAbortController: () => AbortController;
nullifyRef: <T>(ref: React.MutableRefObject<T>) => void;
// Management functions
removeAbortController: (controller: AbortController) => void;
removeCleanup: (cleanup: () => void) => boolean;
triggerCleanup: () => void;
// Debugging
getCleanupStats: () => {
isMounted: boolean;
cleanupFunctions: number;
abortControllers: number;
refsToNullify: number;
};
}
Key Features
isMounted
Prevents state updates after component unmountaddCleanup
Register cleanup functions for automatic executioncreateAbortController
Automatic AbortController management with cleanupnullifyRef
Prevents memory leaks by nullifying refs on unmountStrictMode
Compatible with React StrictMode (effects run twice)Best Practices
Recommended patterns for using tryError hooks effectively
1. Error Boundaries Integration
Combining hooks with error boundariestypescript
import { TryErrorBoundary } from '@try-error/react';
function App() {
return (
<TryErrorBoundary
fallback={(error, errorInfo, retry) => (
<ErrorPage
error={error}
onRetry={retry}
details={errorInfo}
/>
)}
>
<Router>
<Routes>
<Route path="/users" element={<UsersPage />} />
<Route path="/profile" element={<ProfilePage />} />
</Routes>
</Router>
</TryErrorBoundary>
);
}
2. Custom Hook Composition
Creating reusable custom hookstypescript
// Custom hook for API operations
function useApi<T>(endpoint: string) {
return useTry(
async () => {
const result = await tryAsync(() =>
fetch(endpoint).then(r => {
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
})
);
if (isTryError(result)) throw result;
return result;
},
[endpoint],
{ retryCount: 2, retryDelay: 1000 }
);
}
// Usage
function UsersList() {
const { data: users, error, loading } = useApi<User[]>('/api/users');
// Component logic...
}
3. Form Validation Pattern
Form handling with tryErrortypescript
function useFormValidation<T>(validationSchema: (data: T) => TryResult<T>) {
const [data, setData] = useState<T>({} as T);
const [errors, setErrors] = useState<Record<string, string>>({});
const { execute: validate, loading } = useTryCallback(
async (formData: T) => {
const result = validationSchema(formData);
if (isTryError(result)) {
setErrors({ general: result.message });
throw result;
}
setErrors({});
return result;
},
{
onError: (error) => {
if (error.context && typeof error.context === 'object') {
setErrors(error.context as Record<string, string>);
}
}
}
);
return { data, setData, errors, validate, loading };
}
✅ New React Hooks Available
We've added commonly requested React hooks for better integration:
useTryState
State management with built-in error handling
useTryMutation
Mutations with loading states (like React Query)
useFormMutation
Specialized hook for form submissions
useStateWithError
Simple state with error tracking
useTryState Hook
State management with built-in error handling for state updates
Basic Usage
useTryState exampletypescript
function UserProfile() {
const [user, setUser, error, clearError] = useTryState<User | null>(null);
const updateUser = useCallback((newUser: User) => {
setUser(() => {
// This can throw - error will be caught automatically
return validateAndTransformUser(newUser);
});
}, [setUser]);
return (
<div>
{error && (
<div className="error">
{error.message}
<button onClick={clearError}>Clear</button>
</div>
)}
<UserForm
user={user}
onSubmit={updateUser}
/>
</div>
);
}
API Reference
useTryState type signaturetypescript
function useTryState<T>(
initialValue: T
): [
T, // current state value
(updater: T | ((current: T) => T)) => void, // state setter with error handling
TryError | null, // current error (if any)
() => void // clear error function
]
useTryMutation Hook
Mutation handling with loading states, similar to React Query patterns
Basic Usage
useTryMutation exampletypescript
function CreateUserForm() {
const { mutate, isLoading, error, data } = useTryMutation(
async (userData: CreateUserData) => {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
},
{
onSuccess: (user) => {
toast.success(`User ${user.name} created!`);
router.push(`/users/${user.id}`);
},
onError: (error) => {
toast.error(error.message);
}
}
);
const handleSubmit = (formData: CreateUserData) => {
mutate(formData);
};
return (
<form onSubmit={(e) => {
e.preventDefault();
handleSubmit(getFormData(e.currentTarget));
}}>
{/* form fields */}
<button type="submit" disabled={isLoading}>
{isLoading ? 'Creating...' : 'Create User'}
</button>
{error && <div className="error">{error.message}</div>}
{data && <div className="success">User created: {data.name}</div>}
</form>
);
}
API Reference
useTryMutation type signaturetypescript
function useTryMutation<T, TVariables = void>(
mutationFn: (variables: TVariables) => Promise<T>,
options?: {
onSuccess?: (data: T) => void;
onError?: (error: TryError) => void;
onSettled?: () => void;
}
): {
data: T | null;
error: TryError | null;
isLoading: boolean;
isSuccess: boolean;
isError: boolean;
mutate: (variables: TVariables) => Promise<void>;
mutateAsync: (variables: TVariables) => Promise<TryResult<T, TryError>>;
reset: () => void;
}
useFormMutation Hook
Specialized hook for form submissions with automatic FormData handling
Basic Usage
useFormMutation exampletypescript
function ContactForm() {
const { submitForm, isSubmitting, submitError, submitData } = useFormMutation(
async (formData: FormData) => {
const response = await fetch('/api/contact', {
method: 'POST',
body: formData
});
return response.json();
},
{
onSuccess: () => {
toast.success('Message sent successfully!');
}
}
);
return (
<form onSubmit={submitForm}>
<input name="name" placeholder="Your name" required />
<input name="email" type="email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required />
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send Message'}
</button>
{submitError && (
<div className="error">{submitError.message}</div>
)}
{submitData && (
<div className="success">Message sent! ID: {submitData.id}</div>
)}
</form>
);
}