0
# React Hooks
1
2
React hooks for advanced use cases where you need to integrate formatting logic with custom components, complex state management, or non-standard UI patterns. These hooks provide the same formatting logic as the components but give you full control over rendering and behavior.
3
4
## Capabilities
5
6
### useNumericFormat Hook
7
8
React hook that provides numeric formatting logic for building custom components with full control over rendering and state management.
9
10
```typescript { .api }
11
/**
12
* Hook that provides numeric formatting logic for custom components
13
* @param props - NumericFormat configuration options
14
* @returns Props configured for NumberFormatBase component
15
*/
16
function useNumericFormat<BaseType = InputAttributes>(
17
props: NumericFormatProps<BaseType>
18
): NumberFormatBaseProps<BaseType>;
19
```
20
21
**Usage Examples:**
22
23
```typescript
24
import React, { useState } from "react";
25
import { useNumericFormat, NumberFormatBase } from "react-number-format";
26
27
// Custom currency input with validation styling
28
function CurrencyInputWithValidation({ value, onChange, min = 0, max = 1000000 }) {
29
const [isValid, setIsValid] = useState(true);
30
31
const numericProps = useNumericFormat({
32
value,
33
thousandSeparator: true,
34
prefix: "$",
35
decimalScale: 2,
36
fixedDecimalScale: true,
37
onValueChange: (values) => {
38
const amount = values.floatValue || 0;
39
const valid = amount >= min && amount <= max;
40
setIsValid(valid);
41
onChange(values.floatValue);
42
},
43
isAllowed: (values) => {
44
const amount = values.floatValue;
45
return amount === undefined || (amount >= min && amount <= max);
46
}
47
});
48
49
return (
50
<div className={`currency-input-wrapper ${isValid ? 'valid' : 'invalid'}`}>
51
<NumberFormatBase
52
{...numericProps}
53
customInput={(props) => (
54
<input
55
{...props}
56
className={`currency-input ${isValid ? 'border-green' : 'border-red'}`}
57
/>
58
)}
59
/>
60
{!isValid && (
61
<div className="error-message">
62
Amount must be between ${min.toLocaleString()} and ${max.toLocaleString()}
63
</div>
64
)}
65
</div>
66
);
67
}
68
69
// Multi-currency input with dynamic symbols
70
function MultiCurrencyInput({ value, currency, onValueChange, onCurrencyChange }) {
71
const currencyConfig = {
72
USD: { prefix: '$', decimalSeparator: '.', thousandSeparator: ',' },
73
EUR: { prefix: 'โฌ', decimalSeparator: ',', thousandSeparator: '.' },
74
GBP: { prefix: 'ยฃ', decimalSeparator: '.', thousandSeparator: ',' },
75
JPY: { prefix: 'ยฅ', decimalScale: 0, thousandSeparator: ',' }
76
};
77
78
const config = currencyConfig[currency] || currencyConfig.USD;
79
80
const numericProps = useNumericFormat({
81
value,
82
...config,
83
onValueChange: (values) => onValueChange(values.floatValue)
84
});
85
86
return (
87
<div className="multi-currency-input">
88
<select
89
value={currency}
90
onChange={(e) => onCurrencyChange(e.target.value)}
91
className="currency-selector"
92
>
93
{Object.keys(currencyConfig).map(curr => (
94
<option key={curr} value={curr}>{curr}</option>
95
))}
96
</select>
97
<NumberFormatBase {...numericProps} />
98
</div>
99
);
100
}
101
102
// Percentage input with slider integration
103
function PercentageSliderInput({ value, onChange, min = 0, max = 100 }) {
104
const numericProps = useNumericFormat({
105
value,
106
suffix: '%',
107
decimalScale: 1,
108
allowNegative: false,
109
onValueChange: (values) => onChange(values.floatValue || 0),
110
isAllowed: (values) => {
111
const val = values.floatValue;
112
return val === undefined || (val >= min && val <= max);
113
}
114
});
115
116
return (
117
<div className="percentage-input-group">
118
<input
119
type="range"
120
min={min}
121
max={max}
122
value={value || 0}
123
onChange={(e) => onChange(parseFloat(e.target.value))}
124
className="percentage-slider"
125
/>
126
<NumberFormatBase
127
{...numericProps}
128
customInput={(props) => (
129
<input {...props} className="percentage-text-input" />
130
)}
131
/>
132
</div>
133
);
134
}
135
```
136
137
### usePatternFormat Hook
138
139
React hook that provides pattern formatting logic for building custom components with masking and structured input.
140
141
```typescript { .api }
142
/**
143
* Hook that provides pattern formatting logic for custom components
144
* @param props - PatternFormat configuration options
145
* @returns Props configured for NumberFormatBase component
146
*/
147
function usePatternFormat<BaseType = InputAttributes>(
148
props: PatternFormatProps<BaseType>
149
): NumberFormatBaseProps<BaseType>;
150
```
151
152
**Usage Examples:**
153
154
```typescript
155
import React, { useState, useEffect } from "react";
156
import { usePatternFormat, NumberFormatBase } from "react-number-format";
157
158
// Phone input with country code detection
159
function InternationalPhoneInput({ value, onChange, onCountryChange }) {
160
const [country, setCountry] = useState('US');
161
162
const patterns = {
163
US: { format: "(###) ###-####", mask: "_" },
164
UK: { format: "+44 #### ######", mask: "_" },
165
IN: { format: "+91 ##### #####", mask: "_" },
166
DE: { format: "+49 ### #### ####", mask: "_" }
167
};
168
169
// Detect country from input
170
useEffect(() => {
171
if (value?.startsWith('+44')) setCountry('UK');
172
else if (value?.startsWith('+91')) setCountry('IN');
173
else if (value?.startsWith('+49')) setCountry('DE');
174
else setCountry('US');
175
}, [value]);
176
177
const patternProps = usePatternFormat({
178
...patterns[country],
179
value,
180
onValueChange: (values) => {
181
onChange(values.value);
182
onCountryChange?.(country);
183
}
184
});
185
186
return (
187
<div className="international-phone-input">
188
<select
189
value={country}
190
onChange={(e) => {
191
setCountry(e.target.value);
192
onChange(''); // Clear input when country changes
193
}}
194
className="country-selector"
195
>
196
<option value="US">๐บ๐ธ US</option>
197
<option value="UK">๐ฌ๐ง UK</option>
198
<option value="IN">๐ฎ๐ณ India</option>
199
<option value="DE">๐ฉ๐ช Germany</option>
200
</select>
201
<NumberFormatBase {...patternProps} />
202
</div>
203
);
204
}
205
206
// Credit card input with type detection and validation
207
function CreditCardInput({ value, onChange, onCardTypeChange }) {
208
const [cardType, setCardType] = useState('unknown');
209
210
const detectCardType = (number: string) => {
211
const patterns = {
212
visa: /^4/,
213
mastercard: /^5[1-5]/,
214
amex: /^3[47]/,
215
discover: /^6(?:011|5)/
216
};
217
218
for (const [type, pattern] of Object.entries(patterns)) {
219
if (pattern.test(number)) return type;
220
}
221
return 'unknown';
222
};
223
224
const getPattern = (type: string) => {
225
switch (type) {
226
case 'amex': return "#### ###### #####";
227
default: return "#### #### #### ####";
228
}
229
};
230
231
const patternProps = usePatternFormat({
232
format: getPattern(cardType),
233
mask: "_",
234
value,
235
onValueChange: (values) => {
236
const type = detectCardType(values.value);
237
setCardType(type);
238
onCardTypeChange?.(type);
239
onChange(values.value);
240
}
241
});
242
243
return (
244
<div className="credit-card-input">
245
<div className="card-type-indicator">
246
<span className={`card-icon ${cardType}`}>
247
{cardType.toUpperCase()}
248
</span>
249
</div>
250
<NumberFormatBase
251
{...patternProps}
252
customInput={(props) => (
253
<input
254
{...props}
255
className={`card-input ${cardType}`}
256
placeholder={getPattern(cardType).replace(/#/g, '0')}
257
/>
258
)}
259
/>
260
</div>
261
);
262
}
263
264
// Date input with validation and calendar integration
265
function DateInput({ value, onChange, minDate, maxDate }) {
266
const [isValid, setIsValid] = useState(true);
267
268
const validateDate = (dateString: string) => {
269
if (dateString.length !== 8) return false;
270
271
const month = parseInt(dateString.substring(0, 2));
272
const day = parseInt(dateString.substring(2, 4));
273
const year = parseInt(dateString.substring(4, 8));
274
275
const date = new Date(year, month - 1, day);
276
const isValidDate = date.getMonth() === month - 1 &&
277
date.getDate() === day &&
278
date.getFullYear() === year;
279
280
if (!isValidDate) return false;
281
282
if (minDate && date < minDate) return false;
283
if (maxDate && date > maxDate) return false;
284
285
return true;
286
};
287
288
const patternProps = usePatternFormat({
289
format: "##/##/####",
290
mask: ['M', 'M', 'D', 'D', 'Y', 'Y', 'Y', 'Y'],
291
value,
292
allowEmptyFormatting: true,
293
onValueChange: (values) => {
294
const valid = values.value.length === 0 || validateDate(values.value);
295
setIsValid(valid);
296
onChange(values.value);
297
}
298
});
299
300
return (
301
<div className={`date-input-wrapper ${isValid ? 'valid' : 'invalid'}`}>
302
<NumberFormatBase
303
{...patternProps}
304
customInput={(props) => (
305
<input
306
{...props}
307
className={`date-input ${isValid ? '' : 'error'}`}
308
placeholder="MM/DD/YYYY"
309
/>
310
)}
311
/>
312
{!isValid && (
313
<div className="error-message">Please enter a valid date</div>
314
)}
315
</div>
316
);
317
}
318
```
319
320
## Advanced Hook Patterns
321
322
### Combining Multiple Formatters
323
324
```typescript
325
// Component that switches between numeric and pattern formatting
326
function DynamicFormattedInput({ type, value, onChange }) {
327
const numericProps = useNumericFormat({
328
value: type === 'currency' ? value : '',
329
thousandSeparator: true,
330
prefix: '$',
331
decimalScale: 2,
332
onValueChange: (values) => onChange(values.floatValue)
333
});
334
335
const phoneProps = usePatternFormat({
336
format: "(###) ###-####",
337
mask: "_",
338
value: type === 'phone' ? value : '',
339
onValueChange: (values) => onChange(values.value)
340
});
341
342
const ssnProps = usePatternFormat({
343
format: "###-##-####",
344
mask: "_",
345
value: type === 'ssn' ? value : '',
346
onValueChange: (values) => onChange(values.value)
347
});
348
349
const getProps = () => {
350
switch (type) {
351
case 'currency': return numericProps;
352
case 'phone': return phoneProps;
353
case 'ssn': return ssnProps;
354
default: return { value, onValueChange: (v) => onChange(v.value) };
355
}
356
};
357
358
return (
359
<div className="dynamic-input">
360
<select value={type} onChange={(e) => onChange('')}>
361
<option value="text">Text</option>
362
<option value="currency">Currency</option>
363
<option value="phone">Phone</option>
364
<option value="ssn">SSN</option>
365
</select>
366
<NumberFormatBase {...getProps()} />
367
</div>
368
);
369
}
370
```
371
372
### State Management Integration
373
374
```typescript
375
// Integration with Redux or other state managers
376
function ConnectedCurrencyInput({ value, dispatch, fieldName }) {
377
const numericProps = useNumericFormat({
378
value,
379
thousandSeparator: true,
380
prefix: '$',
381
decimalScale: 2,
382
fixedDecimalScale: true,
383
onValueChange: (values) => {
384
dispatch({
385
type: 'UPDATE_FIELD',
386
payload: {
387
field: fieldName,
388
value: values.floatValue,
389
formattedValue: values.formattedValue,
390
isValid: values.floatValue !== undefined
391
}
392
});
393
}
394
});
395
396
return <NumberFormatBase {...numericProps} />;
397
}
398
```
399
400
### Custom Validation Hooks
401
402
```typescript
403
// Hook that combines formatting with validation
404
function useValidatedNumericFormat(props, validator) {
405
const [error, setError] = useState(null);
406
407
const numericProps = useNumericFormat({
408
...props,
409
onValueChange: (values, sourceInfo) => {
410
const validationResult = validator(values);
411
setError(validationResult.error);
412
413
if (props.onValueChange) {
414
props.onValueChange(values, sourceInfo);
415
}
416
}
417
});
418
419
return { numericProps, error };
420
}
421
422
// Usage
423
function ValidatedCurrencyInput({ value, onChange }) {
424
const validator = (values) => {
425
const amount = values.floatValue || 0;
426
if (amount > 10000) {
427
return { error: 'Amount cannot exceed $10,000' };
428
}
429
return { error: null };
430
};
431
432
const { numericProps, error } = useValidatedNumericFormat({
433
value,
434
thousandSeparator: true,
435
prefix: '$',
436
onValueChange: (values) => onChange(values.floatValue)
437
}, validator);
438
439
return (
440
<div>
441
<NumberFormatBase {...numericProps} />
442
{error && <div className="error">{error}</div>}
443
</div>
444
);
445
}
446
```