or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

base-component.mdindex.mdnumeric-formatting.mdpattern-formatting.mdreact-hooks.mdutility-functions.md

react-hooks.mddocs/

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

```