useLoading
Track loading state for async operations with automatic cleanup.
Signature
function useLoading (
initialState ?: boolean
) : [ boolean , WithLoading , ( loading : boolean ) => void ]
type WithLoading = < T >(
promise : undefined | Promise < T | void > | (() => Promise < T | void >)
) => Promise < T | void >
Function to wrap promises with loading state tracking
[2]
(loading: boolean) => void
Manual loading state setter
Usage
Basic Usage
With Return Value
Function Form
import { useLoading } from '@proton/hooks' ;
function MyComponent () {
const [ loading , withLoading ] = useLoading ();
const handleSubmit = async () => {
await withLoading ( async () => {
await api . post ( '/endpoint' , data );
});
};
return < button disabled = { loading } > Submit </ button > ;
}
Features
Automatic cleanup on unmount
Race condition protection (latest call wins)
Supports both promises and promise factories
Type-safe return values
useLoadingByKey
Track loading states for multiple concurrent operations using keys.
Signature
function useLoadingByKey (
initialState ?: LoadingByKey
) : [ LoadingByKey , WithLoadingByKey , ( key : string , loading : boolean ) => void ]
type LoadingByKey = { [ key : string ] : boolean }
type WithLoadingByKey = < T >(
key : string ,
promise : undefined | Promise < T | void > | (() => Promise < T | void >)
) => Promise < T | void >
Initial loading states by key
Usage
import { useLoadingByKey } from '@proton/hooks' ;
function MultiActionComponent () {
const [ loadingMap , withLoadingByKey ] = useLoadingByKey ();
const handleSave = () => withLoadingByKey ( 'save' , saveData ());
const handleDelete = () => withLoadingByKey ( 'delete' , deleteData ());
return (
<>
< button disabled = { loadingMap . save } > Save </ button >
< button disabled = { loadingMap . delete } > Delete </ button >
</>
);
}
useControlled
Handle controlled/uncontrolled component patterns.
Signature
function useControlled < V >(
controlled : V ,
defaultValue ?: V
) : readonly [ V , ( v : V ) => void ]
Controlled value (if undefined, component is uncontrolled)
Default value for uncontrolled mode
Usage
Component Implementation
Controlled Usage
Uncontrolled Usage
import useControlled from '@proton/hooks/useControlled' ;
interface InputProps {
value ?: string ;
defaultValue ?: string ;
onChange ?: ( value : string ) => void ;
}
function Input ({ value , defaultValue , onChange } : InputProps ) {
const [ internalValue , setInternalValue ] = useControlled (
value ,
defaultValue
);
const handleChange = ( newValue : string ) => {
setInternalValue ( newValue );
onChange ?.( newValue );
};
return < input value = { internalValue } onChange = { handleChange } /> ;
}
useCombinedRefs
Combine multiple React refs into a single ref callback.
Signature
function useCombinedRefs < T >(
... refs : ( Ref < T > | undefined )[]
) : Ref < T >
Usage
import useCombinedRefs from '@proton/hooks/useCombinedRefs' ;
import { forwardRef , useRef } from 'react' ;
const MyComponent = forwardRef < HTMLDivElement >(( props , ref ) => {
const internalRef = useRef < HTMLDivElement >( null );
const combinedRef = useCombinedRefs ( ref , internalRef );
return < div ref = { combinedRef } > Content </ div > ;
});
useInstance
Create a stable instance value that persists across renders.
Signature
function useInstance < T >( fn : () => T ) : T
Factory function to create the instance (called only once)
Usage
import useInstance from '@proton/hooks/useInstance' ;
function MyComponent () {
// EventManager is created once and reused
const eventManager = useInstance (() => new EventManager ());
useEffect (() => {
eventManager . subscribe ( 'event' , handler );
}, [ eventManager ]);
return < div > ... </ div > ;
}
useIsMounted
Check if a component is currently mounted.
Signature
function useIsMounted () : () => boolean
Usage
import useIsMounted from '@proton/hooks/useIsMounted' ;
function MyComponent () {
const isMounted = useIsMounted ();
const fetchData = async () => {
const data = await api . fetch ();
// Only update state if component is still mounted
if ( isMounted ()) {
setState ( data );
}
};
return < div > ... </ div > ;
}
useInterval
Declarative interval with automatic cleanup.
Signature
function useInterval (
callback : () => void ,
delay : number | null
) : void
Function to call on each interval
Interval delay in milliseconds (null to pause)
Usage
Basic Usage
Pausable Interval
import useInterval from '@proton/hooks/useInterval' ;
function Timer () {
const [ count , setCount ] = useState ( 0 );
useInterval (() => {
setCount ( c => c + 1 );
}, 1000 );
return < div > Count: { count } </ div > ;
}
useStateRef
Combine useState with useRef for synchronous access to state.
Signature
function useStateRef < S >(
initialState : S | (() => S )
) : [ S , ( value : S ) => void , { current : S }]
Usage
import useStateRef from '@proton/hooks/useStateRef' ;
function MyComponent () {
const [ value , setValue , valueRef ] = useStateRef ( 0 );
const handleAsync = async () => {
await someAsyncOperation ();
// Access latest value synchronously
console . log ( valueRef . current );
};
return < button onClick = { () => setValue ( v => v + 1 ) } > { value } </ button > ;
}
usePrevious
Access the previous value of a variable.
Signature
function usePrevious < T >( value : T ) : T | undefined
Usage
import usePrevious from '@proton/hooks/usePrevious' ;
function MyComponent ({ count } : { count : number }) {
const prevCount = usePrevious ( count );
return (
< div >
Current: { count } , Previous: { prevCount }
</ div >
);
}
useEffectOnce
Run an effect only once on component mount.
Signature
function useEffectOnce ( effect : () => void | (() => void )) : void
Usage
import useEffectOnce from '@proton/hooks/useEffectOnce' ;
function MyComponent () {
useEffectOnce (() => {
console . log ( 'Mounted' );
return () => {
console . log ( 'Unmounted' );
};
});
return < div > ... </ div > ;
}
useSearchParams
Read and write URL search parameters with React Router integration.
Signature
function useSearchParams (
init ?: ParsedSearchParams
) : [ ParsedSearchParams , SetSearchParams ]
type ParsedSearchParams = Record < string , string | string [] | undefined >
type SetSearchParams = (
nextInit : ParsedSearchParams | (( prev : ParsedSearchParams ) => ParsedSearchParams )
) => void
Initial search parameters (applied on mount)
Usage
Reading Parameters
Setting Parameters
Initial Parameters
import useSearchParams from '@proton/hooks/useSearchParams' ;
function MyComponent () {
const [ searchParams ] = useSearchParams ();
const userId = searchParams . userId ;
const filters = searchParams . filters ; // can be string[]
return < div > User ID: { userId } </ div > ;
}
useDateCountdown
Countdown timer to a target date with automatic updates.
Signature
function useDateCountdown (
expiry : Date ,
options ?: DateCountdownOptions
) : DateCountdown
interface DateCountdown {
diff : number ;
days : number ;
hours : number ;
minutes : number ;
seconds : number ;
expired : boolean ;
}
interface DateCountdownOptions {
interval ?: number ;
}
Target date to count down to
Update interval in milliseconds
Usage
import useDateCountdown from '@proton/hooks/useDateCountdown' ;
function CountdownTimer () {
const targetDate = new Date ( '2024-12-31T23:59:59' );
const countdown = useDateCountdown ( targetDate );
if ( countdown . expired ) {
return < div > Time's up! </ div > ;
}
return (
< div >
{ countdown . days } d { countdown . hours } h { countdown . minutes } m { countdown . seconds } s
</ div >
);
}
useAsyncError
Throw errors asynchronously to be caught by Error Boundaries.
Signature
function useAsyncError () : ( error : Error | null ) => void
Usage
import useAsyncError from '@proton/hooks/useAsyncError' ;
function MyComponent () {
const throwError = useAsyncError ();
const fetchData = async () => {
try {
await api . fetch ();
} catch ( error ) {
// This will trigger the nearest Error Boundary
throwError ( error as Error );
}
};
return < button onClick = { fetchData } > Fetch Data </ button > ;
}
This hook allows you to throw errors from async operations so they can be caught by React Error Boundaries.
useStableLoading
Prevent loading state flickering with debounced transitions.
Signature
function useStableLoading (
loadingCondition : boolean [] | boolean | (() => boolean ),
options ?: StableLoadingOptions
) : boolean
interface StableLoadingOptions {
delay ?: number ;
initialState ?: boolean ;
}
loadingCondition
boolean[] | boolean | (() => boolean)
required
Loading condition(s) to stabilize
Delay in milliseconds before clearing loading state
Usage
Single Loading State
Multiple Loading States
Function Form
import useStableLoading from '@proton/hooks/useStableLoading' ;
function MyComponent () {
const [ loading ] = useLoading ();
const stableLoading = useStableLoading ( loading );
// stableLoading stays true for 300ms after loading becomes false
return < Spinner show = { stableLoading } /> ;
}
Use this hook to prevent loading spinners from flickering when operations complete quickly.
useSynchronizingState
State that automatically synchronizes with a prop value.
Signature
function useSynchronizingState < V >( value : V ) : readonly [ V , ( v : V ) => void ]
Value to synchronize with
Usage
import useSynchronizingState from '@proton/hooks/useSynchronizingState' ;
function EditableField ({ initialValue } : { initialValue : string }) {
// State syncs with initialValue when it changes
const [ value , setValue ] = useSynchronizingState ( initialValue );
return (
< input
value = { value }
onChange = { ( e ) => setValue ( e . target . value ) }
/>
);
}
Unlike useState, this hook will update the state whenever the prop value changes (by pointer identity).