async
lifecycle
browser
utilities
Account settings
Update your public profile and preferences.
import type { SubmitEvent } from 'react';
import { useField } from '@siberiacancode/reactuse';
import { CheckIcon, ChevronDownIcon } from 'lucide-react';
import { cn } from '@/utils/lib';
const LANGUAGES = [
{ value: 'en', label: 'English' },
{ value: 'ru', label: 'Russian' },
{ value: 'de', label: 'German' }
];
const EMAIL_PATTERN = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/;
const Demo = () => {
const nameField = useField('siberiacancode', { validateOnBlur: true });
const emailField = useField('hello@reactuse.org', { validateOnBlur: true });
const bioField = useField('Building open-source React hooks ✨');
const languageField = useField('en');
const notificationsField = useField(true);
const publicField = useField(false);
const onSubmit = (event: SubmitEvent<HTMLFormElement>) => {
event.preventDefault();
};
return (
<section className='demo-ui flex w-full max-w-md flex-col gap-1 p-4'>
<div className='mb-3 flex flex-col gap-1'>
<h2 className='text-foreground text-sm font-semibold'>Account settings</h2>
<p className='text-muted-foreground text-xs'>Update your public profile and preferences.</p>
</div>
<form className='flex flex-col gap-4' onSubmit={onSubmit}>
<div className='flex flex-col gap-1.5'>
<label className='text-foreground text-xs font-medium' htmlFor='name'>
Display name
</label>
<input
className='border-border bg-card text-foreground rounded-md border px-3 py-2 text-sm outline-none'
id='name'
placeholder='Your name'
{...nameField.register({
required: 'Name is required',
minLength: { value: 2, message: 'At least 2 characters' }
})}
/>
{nameField.error && <span className='text-destructive text-xs'>{nameField.error}</span>}
</div>
<div className='flex flex-col gap-1.5'>
<label className='text-foreground text-xs font-medium' htmlFor='email'>
Email
</label>
<input
className='border-border bg-card text-foreground rounded-md border px-3 py-2 text-sm outline-none'
id='email'
placeholder='you@example.com'
type='email'
{...emailField.register({
required: 'Email is required',
pattern: { value: EMAIL_PATTERN, message: 'Invalid email format' }
})}
/>
{emailField.error && <span className='text-destructive text-xs'>{emailField.error}</span>}
</div>
<div className='flex flex-col gap-1.5'>
<label className='text-foreground text-xs font-medium' htmlFor='bio'>
Bio
</label>
<textarea
className='border-border bg-card text-foreground min-h-[72px] resize-none rounded-md border px-3 py-2 text-sm outline-none'
id='bio'
placeholder='Tell something about yourself...'
{...bioField.register()}
/>
</div>
<div className='border-border flex flex-col gap-3 border-t pt-4'>
<label className='flex cursor-pointer items-start justify-between gap-3'>
<div className='flex flex-col gap-0.5'>
<span className='text-foreground text-xs font-medium'>Email notifications</span>
<span className='text-muted-foreground text-[11px]'>
Receive product updates and release notes
</span>
</div>
<span className='border-border bg-muted has-[:checked]:bg-foreground has-[:checked]:border-foreground relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border transition-colors'>
<input className='peer sr-only' type='checkbox' {...notificationsField.register()} />
<span className='bg-background absolute left-0.5 size-3.5 rounded-full transition-transform peer-checked:translate-x-4' />
</span>
</label>
<div className='flex items-center justify-between gap-3'>
<div className='flex flex-col gap-0.5'>
<label className='text-foreground text-xs font-medium' htmlFor='language'>
Language
</label>
<span className='text-muted-foreground text-[11px]'>
Choose your preferred language
</span>
</div>
<div className='relative'>
<select
className='border-border bg-card text-foreground w-32 appearance-none rounded-md border py-1.5 pr-7 pl-3 text-xs outline-none'
id='language'
{...languageField.register()}
>
{LANGUAGES.map((language) => (
<option key={language.value} value={language.value}>
{language.label}
</option>
))}
</select>
<ChevronDownIcon className='text-muted-foreground pointer-events-none absolute top-1/2 right-2 size-3.5 -translate-y-1/2' />
</div>
</div>
</div>
<label className='flex cursor-pointer items-start gap-3'>
<span
className={cn(
'border-input has-[:checked]:bg-foreground has-[:checked]:border-foreground relative mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-[4px] border transition-colors'
)}
>
<input className='peer sr-only' type='checkbox' {...publicField.register()} />
<CheckIcon
className='text-background size-3 opacity-0 peer-checked:opacity-100'
strokeWidth={3}
/>
</span>
<div className='flex flex-col gap-0.5'>
<span className='text-foreground text-xs font-medium'>Make profile public</span>
<span className='text-muted-foreground text-[11px]'>
Anyone on the internet can see your profile
</span>
</div>
</label>
<div className='flex justify-end'>
<button type='submit'>Save changes</button>
</div>
</form>
</section>
);
};
export default Demo;
Installation
pnpm add @siberiacancode/reactuseUsage
const { register, getValue, setValue, reset, dirty, error, setError, clearError, touched, focus, watch } = useField();Type Declarations
import type { RefObject } from 'react';
export interface UseFieldOptions {
/** The auto focus */
autoFocus?: boolean;
/** The initial touched */
initialTouched?: boolean;
/** The validate on blur */
validateOnBlur?: boolean;
/** The validate on mount */
validateOnChange?: boolean;
/** The validate on mount */
validateOnMount?: boolean;
}
export interface UseFieldRegisterParams {
/** The min value validation */
max?: {
value: number;
message: string;
};
/** The max length validation */
maxLength?: {
value: number;
message: string;
};
/** The max value validation */
min?: {
value: number;
message: string;
};
/** The min length validation */
minLength?: {
value: number;
message: string;
};
/** The pattern validation */
pattern?: {
value: RegExp;
message: string;
};
/** The required validation */
required?: string;
/** The custom validation */
validate?: (value: string) => string | true | Promise<string | true>;
}
export interface UseFieldReturn<Value> {
/** The dirty state */
dirty: boolean;
/** The error state */
error?: string;
/** The input ref */
ref: RefObject<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | null>;
/** The set error function */
touched: boolean;
/** The set error function */
clearError: () => void;
/** The focus function */
focus: () => void;
/** The get value function */
getValue: () => Value;
/** The register function */
register: (params?: UseFieldRegisterParams) => {
onBlur: () => void;
onChange: () => void;
ref: (
node: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | null | undefined
) => void;
};
/** The reset function */
reset: () => void;
/** The set error function */
setError: (error: string) => void;
/** The set value function */
setValue: (value: Value) => void;
/** The watch function */
watch: () => Value;
}API
Parameters
| Name | Type | Default | Note |
|---|---|---|---|
| params.initialValue | Value | "" | Initial value |
| params.initialTouched | boolean | false | Initial touched state |
| params.autoFocus | boolean | false | Auto focus |
| params.validateOnChange | boolean | false | Validate on change |
| params.validateOnBlur | boolean | false | Validate on blur |
| params.validateOnMount | boolean | false | Validate on mount |
Returns
UseFieldReturn<Value>Contributors
ddebabinhhywax
Last updated on