or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-proxy.mdindex.mdreact-integration.mdutilities.md

utilities.mddocs/

0

# Advanced Utilities

1

2

Extended functionality including reactive effects, key-specific subscriptions, DevTools integration, deep cloning, and specialized collections for Maps and Sets.

3

4

## Capabilities

5

6

### Reactive Effects (watch)

7

8

Creates a reactive effect that automatically tracks proxy objects and re-evaluates whenever tracked objects update. Provides Vue.js-style computed effects for Valtio.

9

10

```typescript { .api }

11

/**

12

* Creates a reactive effect that automatically tracks proxy objects

13

* Callback is invoked immediately to detect tracked objects

14

* @param callback - Function that receives a get function for tracking proxy objects

15

* @param options - Configuration options

16

* @param options.sync - If true, notifications happen synchronously

17

* @returns Cleanup function to stop the reactive effect

18

*/

19

function watch(

20

callback: (get: <T extends object>(proxyObject: T) => T) => void | (() => void) | Promise<void | (() => void)>,

21

options?: { sync?: boolean }

22

): () => void;

23

```

24

25

**Usage Examples:**

26

27

```typescript

28

import { proxy, watch } from "valtio/utils";

29

30

const state = proxy({ count: 0, multiplier: 2 });

31

32

// Basic reactive effect

33

const cleanup = watch((get) => {

34

const { count, multiplier } = get(state);

35

console.log("Computed value:", count * multiplier);

36

});

37

38

state.count = 5; // Logs: "Computed value: 10"

39

state.multiplier = 3; // Logs: "Computed value: 15"

40

41

// Effect with cleanup

42

const cleanupWithSideEffect = watch((get) => {

43

const { count } = get(state);

44

45

// Set up side effect

46

const timer = setInterval(() => {

47

console.log("Current count:", count);

48

}, 1000);

49

50

// Return cleanup function

51

return () => clearInterval(timer);

52

});

53

54

// Nested watch calls are automatically cleaned up

55

watch((get) => {

56

const { user } = get(appState);

57

58

if (user) {

59

// This watch will be cleaned up when user changes

60

const innerCleanup = watch((get) => {

61

const { preferences } = get(user);

62

console.log("User preferences:", preferences);

63

});

64

}

65

});

66

67

// Stop watching

68

cleanup();

69

```

70

71

### Key-Specific Subscriptions

72

73

Subscribes to changes of a specific property key, providing more efficient notifications than general subscriptions.

74

75

```typescript { .api }

76

/**

77

* Subscribes to a specific property key of a proxy object

78

* Only fires when the specified property changes

79

* @param proxyObject - The proxy object to subscribe to

80

* @param key - The property key to watch

81

* @param callback - Function called when the key changes

82

* @param notifyInSync - If true, notifications happen synchronously

83

* @returns Unsubscribe function

84

*/

85

function subscribeKey<T extends object, K extends keyof T>(

86

proxyObject: T,

87

key: K,

88

callback: (value: T[K]) => void,

89

notifyInSync?: boolean

90

): () => void;

91

```

92

93

**Usage Examples:**

94

95

```typescript

96

import { proxy, subscribeKey } from "valtio/utils";

97

98

const state = proxy({ count: 0, name: "Alice", theme: "light" });

99

100

// Subscribe to specific key

101

const unsubscribe = subscribeKey(state, "count", (newCount) => {

102

console.log("Count changed to:", newCount);

103

});

104

105

state.count++; // Logs: "Count changed to: 1"

106

state.name = "Bob"; // No log (different key)

107

state.count = 5; // Logs: "Count changed to: 5"

108

109

// Multiple subscriptions to same key

110

const unsub1 = subscribeKey(state, "theme", (theme) => {

111

document.body.className = theme;

112

});

113

114

const unsub2 = subscribeKey(state, "theme", (theme) => {

115

localStorage.setItem("theme", theme);

116

});

117

118

state.theme = "dark"; // Both callbacks fire

119

120

// Cleanup

121

unsubscribe();

122

unsub1();

123

unsub2();

124

```

125

126

### Redux DevTools Integration

127

128

Connects a proxy object to Redux DevTools Extension for state debugging and time-travel debugging.

129

130

```typescript { .api }

131

/**

132

* Connects a proxy object to Redux DevTools Extension

133

* Enables real-time monitoring and time-travel debugging

134

* Limitation: Only plain objects/values are supported

135

* @param proxyObject - The proxy object to connect to DevTools

136

* @param options - Configuration options for the DevTools connection

137

* @param options.enabled - Explicitly enable or disable the connection

138

* @param options.name - Name to display in DevTools

139

* @returns Unsubscribe function or undefined if connection failed

140

*/

141

function devtools<T extends object>(

142

proxyObject: T,

143

options?: {

144

enabled?: boolean;

145

name?: string;

146

[key: string]: any; // Additional Redux DevTools options

147

}

148

): (() => void) | undefined;

149

```

150

151

**Usage Examples:**

152

153

```typescript

154

import { proxy, devtools } from "valtio/utils";

155

156

const state = proxy({ count: 0, user: { name: "Alice" } });

157

158

// Basic DevTools connection

159

const disconnect = devtools(state, {

160

name: "App State",

161

enabled: true

162

});

163

164

// DevTools will show:

165

// - Current state snapshots

166

// - Action history with operation details

167

// - Time-travel debugging capabilities

168

169

state.count++; // Shows as "set:count" in DevTools

170

state.user.name = "Bob"; // Shows as "set:user.name" in DevTools

171

172

// Multiple stores

173

const userState = proxy({ profile: {}, settings: {} });

174

const appState = proxy({ theme: "light", language: "en" });

175

176

devtools(userState, { name: "User Store" });

177

devtools(appState, { name: "App Store" });

178

179

// Conditional enabling

180

devtools(state, {

181

enabled: process.env.NODE_ENV === "development",

182

name: "Debug State"

183

});

184

185

// Cleanup

186

disconnect?.();

187

```

188

189

### Deep Cloning

190

191

Creates a deep clone of an object while maintaining proxy behavior for Maps and Sets.

192

193

```typescript { .api }

194

/**

195

* Creates a deep clone of an object, maintaining proxy behavior for Maps and Sets

196

* @param obj - The object to clone

197

* @param getRefSet - Function to get the set of reference objects (optional)

198

* @returns A deep clone of the input object

199

*/

200

function deepClone<T>(

201

obj: T,

202

getRefSet?: () => WeakSet<object>

203

): T;

204

```

205

206

**Usage Examples:**

207

208

```typescript

209

import { proxy, deepClone, ref } from "valtio";

210

import { deepClone } from "valtio/utils";

211

212

const originalState = proxy({

213

user: { name: "Alice", preferences: { theme: "dark" } },

214

items: [1, 2, { nested: true }],

215

metadata: ref({ immutable: "data" })

216

});

217

218

// Deep clone the state

219

const clonedState = deepClone(originalState);

220

221

// Cloned state is independent

222

clonedState.user.name = "Bob";

223

console.log(originalState.user.name); // Still "Alice"

224

225

// Referenced objects are preserved

226

console.log(clonedState.metadata === originalState.metadata); // true

227

228

// Works with complex structures

229

const complexState = proxy({

230

map: new Map([["key", "value"]]),

231

set: new Set([1, 2, 3]),

232

date: new Date(),

233

nested: { deep: { structure: "value" } }

234

});

235

236

const cloned = deepClone(complexState);

237

```

238

239

### Proxy Sets

240

241

Creates a reactive Set that integrates with Valtio's proxy system, extending the standard Set API with additional set operations.

242

243

```typescript { .api }

244

/**

245

* Creates a reactive Set that integrates with Valtio's proxy system

246

* Includes extended set operations like union, intersection, difference

247

* @param initialValues - Initial values to populate the Set

248

* @returns A reactive proxy Set with extended methods

249

* @throws TypeError if initialValues is not iterable

250

*/

251

function proxySet<T>(initialValues?: Iterable<T> | null): ProxySet<T>;

252

253

/**

254

* Determines if an object is a proxy Set created with proxySet

255

* @param obj - The object to check

256

* @returns True if the object is a proxy Set

257

*/

258

function isProxySet(obj: object): boolean;

259

260

```

261

262

**Usage Examples:**

263

264

```typescript

265

import { proxy, useSnapshot } from "valtio";

266

import { proxySet, isProxySet } from "valtio/utils";

267

268

// Basic usage

269

const tags = proxySet(["javascript", "react", "typescript"]);

270

271

// Use within proxy state

272

const state = proxy({

273

selectedTags: proxySet<string>(),

274

allTags: proxySet(["javascript", "react", "vue", "typescript"])

275

});

276

277

// React component

278

function TagSelector() {

279

const { selectedTags, allTags } = useSnapshot(state);

280

281

return (

282

<div>

283

{[...allTags].map(tag => (

284

<button

285

key={tag}

286

onClick={() => {

287

if (state.selectedTags.has(tag)) {

288

state.selectedTags.delete(tag);

289

} else {

290

state.selectedTags.add(tag);

291

}

292

}}

293

className={selectedTags.has(tag) ? "selected" : ""}

294

>

295

{tag}

296

</button>

297

))}

298

</div>

299

);

300

}

301

302

// Extended set operations

303

const set1 = proxySet([1, 2, 3, 4]);

304

const set2 = proxySet([3, 4, 5, 6]);

305

306

const union = set1.union(set2); // {1, 2, 3, 4, 5, 6}

307

const intersection = set1.intersection(set2); // {3, 4}

308

const difference = set1.difference(set2); // {1, 2}

309

const symmetric = set1.symmetricDifference(set2); // {1, 2, 5, 6}

310

311

console.log(set1.isSubsetOf(set2)); // false

312

console.log(set1.isSupersetOf(new Set([1, 2]))); // true

313

console.log(set1.isDisjointFrom(new Set([7, 8]))); // true

314

315

// Type checking

316

console.log(isProxySet(set1)); // true

317

console.log(isProxySet(new Set())); // false

318

```

319

320

### Proxy Maps

321

322

Creates a reactive Map that integrates with Valtio's proxy system with the same API as standard JavaScript Map.

323

324

```typescript { .api }

325

/**

326

* Creates a reactive Map that integrates with Valtio's proxy system

327

* The API is the same as the standard JavaScript Map

328

* @param entries - Initial key-value pairs to populate the Map

329

* @returns A proxy Map object that tracks changes

330

* @throws TypeError if entries is not iterable

331

*/

332

function proxyMap<K, V>(entries?: Iterable<[K, V]> | null): ProxyMap<K, V>;

333

334

/**

335

* Determines if an object is a proxy Map created with proxyMap

336

* @param obj - The object to check

337

* @returns True if the object is a proxy Map

338

*/

339

function isProxyMap(obj: object): boolean;

340

```

341

342

**Usage Examples:**

343

344

```typescript

345

import { proxy, useSnapshot, ref } from "valtio";

346

import { proxyMap, isProxyMap } from "valtio/utils";

347

348

// Basic usage

349

const cache = proxyMap<string, any>();

350

351

// Use within proxy state

352

const state = proxy({

353

userCache: proxyMap<number, User>(),

354

settings: proxyMap([

355

["theme", "dark"],

356

["language", "en"]

357

])

358

});

359

360

// React component

361

function UserList() {

362

const { userCache } = useSnapshot(state);

363

364

return (

365

<div>

366

{[...userCache.entries()].map(([id, user]) => (

367

<div key={id}>

368

{user.name}

369

<button onClick={() => state.userCache.delete(id)}>

370

Remove

371

</button>

372

</div>

373

))}

374

</div>

375

);

376

}

377

378

// Standard Map operations

379

state.userCache.set(1, { name: "Alice", age: 25 });

380

state.userCache.set(2, { name: "Bob", age: 30 });

381

382

console.log(state.userCache.get(1)); // { name: "Alice", age: 25 }

383

console.log(state.userCache.size); // 2

384

385

// Using object keys with ref

386

const objKey = ref({ id: "special" });

387

state.userCache.set(objKey, { name: "Special User", age: 35 });

388

389

// Without ref, object keys might not work as expected

390

const badKey = { id: "bad" };

391

state.userCache.set(badKey, { name: "Bad User", age: 40 });

392

console.log(state.userCache.get(badKey)); // undefined (key equality issue)

393

394

// Iteration

395

for (const [key, value] of state.userCache) {

396

console.log(`User ${key}:`, value.name);

397

}

398

399

// Type checking

400

console.log(isProxyMap(state.userCache)); // true

401

console.log(isProxyMap(new Map())); // false

402

```

403

404

## Utility Types

405

406

```typescript { .api }

407

type WatchCallback = (

408

get: <T extends object>(proxyObject: T) => T

409

) => void | (() => void) | Promise<void | (() => void)>;

410

411

type WatchOptions = {

412

sync?: boolean;

413

};

414

415

type Cleanup = () => void;

416

417

interface DevToolsOptions {

418

enabled?: boolean;

419

name?: string;

420

[key: string]: any;

421

}

422

423

// Extended collection interfaces

424

interface ProxySet<T> extends Set<T> {

425

// Extended set operations

426

intersection(other: Set<T>): Set<T>;

427

union(other: Set<T>): Set<T>;

428

difference(other: Set<T>): Set<T>;

429

symmetricDifference(other: Set<T>): Set<T>;

430

// Set comparison methods

431

isSubsetOf(other: Set<T>): boolean;

432

isSupersetOf(other: Set<T>): boolean;

433

isDisjointFrom(other: Set<T>): boolean;

434

// Additional methods

435

toJSON(): Set<T>;

436

}

437

438

interface ProxyMap<K, V> extends Map<K, V> {

439

toJSON(): Map<K, V>;

440

}

441

442

// Additional internal types

443

type AnyFunction = (...args: any[]) => any;

444

type ProxyObject = object;

445

type Path = (string | symbol)[];

446

```