or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

browser-apis.mddevice.mddom-events.mdindex.mdnavigation.mdnetwork.mdobservers.mdspecialized.mdstate-management.mdstorage.mdtiming.mdutilities.md

utilities.mddocs/

0

# Lifecycle & Utilities

1

2

React lifecycle utilities and helper hooks for component lifecycle management, ref handling, and common utility patterns.

3

4

## Capabilities

5

6

### useForceUpdate

7

8

Force component re-render when needed.

9

10

```typescript { .api }

11

/**

12

* Force component re-render

13

* @returns Function to trigger re-render

14

*/

15

function useForceUpdate(): () => void;

16

```

17

18

**Usage Examples:**

19

20

```typescript

21

import { useForceUpdate } from "@mantine/hooks";

22

23

function ComponentWithExternalState() {

24

const forceUpdate = useForceUpdate();

25

26

// Force update when external state changes

27

useEffect(() => {

28

const unsubscribe = externalStore.subscribe(() => {

29

forceUpdate();

30

});

31

return unsubscribe;

32

}, [forceUpdate]);

33

34

return <div>{externalStore.getValue()}</div>;

35

}

36

```

37

38

### useId

39

40

Generate unique IDs with SSR support and optional static override.

41

42

```typescript { .api }

43

/**

44

* Generate unique IDs with SSR support

45

* @param staticId - Static ID to use instead of generated one

46

* @returns Unique ID string

47

*/

48

function useId(staticId?: string): string;

49

```

50

51

**Usage Examples:**

52

53

```typescript

54

import { useId } from "@mantine/hooks";

55

56

function FormField({ label, staticId }: { label: string; staticId?: string }) {

57

const id = useId(staticId);

58

59

return (

60

<div>

61

<label htmlFor={id}>{label}</label>

62

<input id={id} />

63

</div>

64

);

65

}

66

67

// Multiple IDs in same component

68

function MultiFieldForm() {

69

const nameId = useId();

70

const emailId = useId();

71

72

return (

73

<form>

74

<label htmlFor={nameId}>Name</label>

75

<input id={nameId} />

76

77

<label htmlFor={emailId}>Email</label>

78

<input id={emailId} />

79

</form>

80

);

81

}

82

```

83

84

### useMounted

85

86

Track component mount state.

87

88

```typescript { .api }

89

/**

90

* Track component mount state

91

* @returns Boolean indicating if component is mounted

92

*/

93

function useMounted(): boolean;

94

```

95

96

**Usage Examples:**

97

98

```typescript

99

import { useMounted } from "@mantine/hooks";

100

101

function AsyncComponent() {

102

const [data, setData] = useState(null);

103

const mounted = useMounted();

104

105

useEffect(() => {

106

fetchData().then(result => {

107

// Only update state if component is still mounted

108

if (mounted) {

109

setData(result);

110

}

111

});

112

}, [mounted]);

113

114

return <div>{data ? 'Loaded' : 'Loading...'}</div>;

115

}

116

```

117

118

### useIsFirstRender

119

120

Detect if current render is the first render.

121

122

```typescript { .api }

123

/**

124

* Detect first render

125

* @returns Boolean indicating if this is the first render

126

*/

127

function useIsFirstRender(): boolean;

128

```

129

130

**Usage Examples:**

131

132

```typescript

133

import { useIsFirstRender } from "@mantine/hooks";

134

135

function AnimatedComponent() {

136

const isFirstRender = useIsFirstRender();

137

138

return (

139

<div

140

className={isFirstRender ? 'no-animation' : 'with-animation'}

141

>

142

Content

143

</div>

144

);

145

}

146

```

147

148

### usePrevious

149

150

Access the previous value of a variable.

151

152

```typescript { .api }

153

/**

154

* Access previous value

155

* @param value - Current value

156

* @returns Previous value or undefined on first render

157

*/

158

function usePrevious<T>(value: T): T | undefined;

159

```

160

161

**Usage Examples:**

162

163

```typescript

164

import { usePrevious } from "@mantine/hooks";

165

166

function Counter() {

167

const [count, setCount] = useState(0);

168

const previousCount = usePrevious(count);

169

170

return (

171

<div>

172

<p>Current: {count}</p>

173

<p>Previous: {previousCount ?? 'None'}</p>

174

<button onClick={() => setCount(c => c + 1)}>

175

Increment

176

</button>

177

</div>

178

);

179

}

180

181

// Track prop changes

182

function UserProfile({ userId }: { userId: string }) {

183

const previousUserId = usePrevious(userId);

184

185

useEffect(() => {

186

if (previousUserId && previousUserId !== userId) {

187

console.log(`User changed from ${previousUserId} to ${userId}`);

188

}

189

}, [userId, previousUserId]);

190

191

return <div>User ID: {userId}</div>;

192

}

193

```

194

195

### useDidUpdate

196

197

Effect that skips the first render (componentDidUpdate equivalent).

198

199

```typescript { .api }

200

/**

201

* Effect that skips first render

202

* @param fn - Effect function

203

* @param dependencies - Dependency array

204

*/

205

function useDidUpdate(fn: React.EffectCallback, dependencies?: React.DependencyList): void;

206

```

207

208

**Usage Examples:**

209

210

```typescript

211

import { useDidUpdate } from "@mantine/hooks";

212

213

function SearchComponent({ query }: { query: string }) {

214

const [results, setResults] = useState([]);

215

216

// Only run search on updates, not on mount

217

useDidUpdate(() => {

218

if (query) {

219

searchAPI(query).then(setResults);

220

}

221

}, [query]);

222

223

return <SearchResults results={results} />;

224

}

225

```

226

227

### useIsomorphicEffect

228

229

useLayoutEffect in browser, useEffect in SSR environments.

230

231

```typescript { .api }

232

/**

233

* Isomorphic effect (useLayoutEffect in browser, useEffect in SSR)

234

* @param effect - Effect function

235

* @param deps - Dependency array

236

*/

237

function useIsomorphicEffect(effect: React.EffectCallback, deps?: React.DependencyList): void;

238

```

239

240

### useShallowEffect

241

242

Effect with shallow dependency comparison instead of reference equality.

243

244

```typescript { .api }

245

/**

246

* Effect with shallow dependency comparison

247

* @param callback - Effect function

248

* @param dependencies - Dependency array (compared shallowly)

249

*/

250

function useShallowEffect(callback: React.EffectCallback, dependencies: React.DependencyList): void;

251

```

252

253

**Usage Examples:**

254

255

```typescript

256

import { useShallowEffect } from "@mantine/hooks";

257

258

function ObjectDependentComponent({ config }: { config: Config }) {

259

const [data, setData] = useState(null);

260

261

// Only re-run when config properties change, not reference

262

useShallowEffect(() => {

263

fetchDataWithConfig(config).then(setData);

264

}, [config]);

265

266

return <div>{data}</div>;

267

}

268

```

269

270

### useMergedRef

271

272

Merge multiple refs into a single ref callback.

273

274

```typescript { .api }

275

/**

276

* Merge multiple refs into single ref callback

277

* @param refs - Array of refs to merge

278

* @returns Single ref callback

279

*/

280

function useMergedRef<T>(...refs: React.Ref<T>[]): React.RefCallback<T | null>;

281

282

/**

283

* Merge refs utility function

284

* @param refs - Array of refs to merge

285

* @returns Single ref callback

286

*/

287

function mergeRefs<T>(...refs: React.Ref<T>[]): React.RefCallback<T | null>;

288

289

/**

290

* Assign value to ref utility

291

* @param ref - Ref to assign to

292

* @param value - Value to assign

293

*/

294

function assignRef<T>(ref: React.Ref<T> | undefined, value: T): void;

295

```

296

297

**Usage Examples:**

298

299

```typescript

300

import { useMergedRef } from "@mantine/hooks";

301

import { useRef, forwardRef } from "react";

302

303

// Merge internal ref with forwarded ref

304

const CustomInput = forwardRef<HTMLInputElement, Props>((props, forwardedRef) => {

305

const internalRef = useRef<HTMLInputElement>(null);

306

const mergedRef = useMergedRef(internalRef, forwardedRef);

307

308

useEffect(() => {

309

// Use internal ref for internal logic

310

if (internalRef.current) {

311

internalRef.current.focus();

312

}

313

}, []);

314

315

return <input ref={mergedRef} {...props} />;

316

});

317

318

// Merge multiple hook refs

319

function MultiRefComponent() {

320

const hoverRef = useHover().ref;

321

const clickOutsideRef = useClickOutside(() => {});

322

const focusWithinRef = useFocusWithin().ref;

323

324

const mergedRef = useMergedRef(hoverRef, clickOutsideRef, focusWithinRef);

325

326

return <div ref={mergedRef}>Multiple behaviors</div>;

327

}

328

```

329

330

## Utility Functions

331

332

Core utility functions exported by the package.

333

334

```typescript { .api }

335

/**

336

* Clamp value between min and max

337

* @param value - Value to clamp

338

* @param min - Minimum value

339

* @param max - Maximum value

340

* @returns Clamped value

341

*/

342

function clamp(value: number, min: number, max: number): number;

343

344

/**

345

* Generate random ID string

346

* @returns Random ID

347

*/

348

function randomId(): string;

349

350

/**

351

* Create array of numbers in range

352

* @param start - Start number (inclusive)

353

* @param end - End number (exclusive)

354

* @returns Array of numbers

355

*/

356

function range(start: number, end: number): number[];

357

358

/**

359

* Shallow equality comparison

360

* @param a - First value

361

* @param b - Second value

362

* @returns Boolean indicating shallow equality

363

*/

364

function shallowEqual(a: any, b: any): boolean;

365

366

/**

367

* Convert first character to uppercase

368

* @param string - Input string

369

* @returns String with first character uppercased

370

*/

371

function upperFirst(string: string): string;

372

373

/**

374

* Convert first character to lowercase

375

* @param string - Input string

376

* @returns String with first character lowercased

377

*/

378

function lowerFirst(string: string): string;

379

380

/**

381

* Ref callback hook utility

382

* @param callback - Callback to convert to ref

383

* @returns Ref callback

384

*/

385

function useCallbackRef<T>(callback: T | undefined): T;

386

```

387

388

**Usage Examples:**

389

390

```typescript

391

import { clamp, randomId, range, shallowEqual, upperFirst } from "@mantine/hooks";

392

393

// Mathematical utilities

394

const percentage = clamp(userInput, 0, 100); // Ensure 0-100 range

395

const numbers = range(1, 10); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

396

397

// String utilities

398

const title = upperFirst('hello world'); // "Hello world"

399

400

// Comparison

401

const hasChanged = !shallowEqual(prevProps, nextProps);

402

403

// Unique IDs

404

const elementId = randomId(); // "mantine-r4nd0m1d"

405

```

406

407

## Common Patterns

408

409

### Ref Management

410

411

```typescript

412

import { useMergedRef, useCallbackRef } from "@mantine/hooks";

413

414

function ComplexComponent({ onResize, onClick }: Props) {

415

const resizeRef = useResizeObserver(onResize);

416

const clickRef = useClickOutside(onClick);

417

const internalRef = useRef<HTMLDivElement>(null);

418

419

// Merge all refs

420

const mergedRef = useMergedRef(resizeRef, clickRef, internalRef);

421

422

return <div ref={mergedRef}>Complex component</div>;

423

}

424

```

425

426

### Lifecycle Management

427

428

```typescript

429

import { useMounted, useDidUpdate, usePrevious } from "@mantine/hooks";

430

431

function DataComponent({ dataId }: { dataId: string }) {

432

const [data, setData] = useState(null);

433

const [loading, setLoading] = useState(false);

434

const mounted = useMounted();

435

const previousDataId = usePrevious(dataId);

436

437

// Load data on mount

438

useEffect(() => {

439

loadData(dataId);

440

}, []);

441

442

// Reload data when ID changes (skip first render)

443

useDidUpdate(() => {

444

if (previousDataId !== dataId) {

445

loadData(dataId);

446

}

447

}, [dataId, previousDataId]);

448

449

const loadData = async (id: string) => {

450

setLoading(true);

451

try {

452

const result = await fetchData(id);

453

if (mounted) {

454

setData(result);

455

}

456

} finally {

457

if (mounted) {

458

setLoading(false);

459

}

460

}

461

};

462

463

return loading ? <div>Loading...</div> : <div>{data}</div>;

464

}

465

```