or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

field.mdform-spy.mdform.mdhooks.mdindex.mdtypescript.md

form-spy.mddocs/

0

# Form Spy

1

2

FormSpy component for observing form state changes without rendering form fields, perfect for external components that need form state updates.

3

4

## Capabilities

5

6

### FormSpy Component

7

8

Component that subscribes to form state changes and renders UI based on form state without rendering actual form fields.

9

10

```typescript { .api }

11

/**

12

* Component for observing form state without rendering form fields

13

* @param props - FormSpy configuration and render props

14

* @returns React element or null

15

*/

16

const FormSpy: <FormValues = Record<string, any>>(

17

props: FormSpyProps<FormValues>

18

) => React.ReactElement;

19

20

interface FormSpyProps<FormValues = Record<string, any>>

21

extends UseFormStateParams<FormValues>,

22

RenderableProps<FormSpyRenderProps<FormValues>> {}

23

24

interface FormSpyRenderProps<FormValues = Record<string, any>>

25

extends FormState<FormValues> {

26

/** Form API instance for programmatic control */

27

form: FormApi<FormValues>;

28

}

29

30

interface UseFormStateParams<FormValues = Record<string, any>> {

31

/** Callback when form state changes */

32

onChange?: (formState: FormState<FormValues>) => void;

33

/** Form state subscription configuration */

34

subscription?: FormSubscription;

35

}

36

```

37

38

**Usage Examples:**

39

40

```typescript

41

import React from "react";

42

import { Form, Field, FormSpy } from "react-final-form";

43

44

// Basic FormSpy usage

45

function BasicFormSpy() {

46

return (

47

<Form onSubmit={(values) => console.log(values)}>

48

{({ handleSubmit }) => (

49

<form onSubmit={handleSubmit}>

50

<Field name="firstName" component="input" />

51

52

<FormSpy>

53

{({ values, dirty, invalid }) => (

54

<div>

55

<p>Form is {dirty ? "dirty" : "pristine"}</p>

56

<p>Form is {invalid ? "invalid" : "valid"}</p>

57

<pre>{JSON.stringify(values, null, 2)}</pre>

58

</div>

59

)}

60

</FormSpy>

61

</form>

62

)}

63

</Form>

64

);

65

}

66

67

// FormSpy with subscription

68

function OptimizedFormSpy() {

69

return (

70

<Form onSubmit={(values) => console.log(values)}>

71

{({ handleSubmit }) => (

72

<form onSubmit={handleSubmit}>

73

<Field name="email" component="input" type="email" />

74

75

<FormSpy subscription={{ values: true, submitting: true }}>

76

{({ values, submitting }) => (

77

<div>

78

{submitting && <p>Submitting...</p>}

79

<p>Email: {values.email}</p>

80

</div>

81

)}

82

</FormSpy>

83

</form>

84

)}

85

</Form>

86

);

87

}

88

89

// FormSpy with onChange callback

90

function CallbackFormSpy() {

91

const handleFormChange = (state: any) => {

92

console.log("Form state changed:", state);

93

// Save to localStorage, send analytics, etc.

94

};

95

96

return (

97

<Form onSubmit={(values) => console.log(values)}>

98

{({ handleSubmit }) => (

99

<form onSubmit={handleSubmit}>

100

<Field name="username" component="input" />

101

102

<FormSpy onChange={handleFormChange} />

103

</form>

104

)}

105

</Form>

106

);

107

}

108

```

109

110

### FormSpy with Form Control

111

112

FormSpy provides access to the form API for programmatic form control.

113

114

```typescript { .api }

115

/**

116

* FormSpy render props include form API for programmatic control

117

*/

118

interface FormSpyRenderProps<FormValues = Record<string, any>>

119

extends FormState<FormValues> {

120

/** Complete form API instance */

121

form: FormApi<FormValues>;

122

}

123

```

124

125

**Usage Examples:**

126

127

```typescript

128

// FormSpy with form control buttons

129

function FormControlSpy() {

130

return (

131

<Form onSubmit={(values) => console.log(values)}>

132

{({ handleSubmit }) => (

133

<form onSubmit={handleSubmit}>

134

<Field name="message" component="textarea" />

135

136

<FormSpy>

137

{({ form, pristine, invalid, values }) => (

138

<div>

139

<button

140

type="button"

141

onClick={() => form.reset()}

142

disabled={pristine}

143

>

144

Reset Form

145

</button>

146

147

<button

148

type="button"

149

onClick={() => form.change("message", "Hello World")}

150

>

151

Set Default Message

152

</button>

153

154

<button

155

type="button"

156

onClick={() => form.focus("message")}

157

>

158

Focus Message

159

</button>

160

161

<pre>{JSON.stringify(values, null, 2)}</pre>

162

</div>

163

)}

164

</FormSpy>

165

</form>

166

)}

167

</Form>

168

);

169

}

170

171

// FormSpy for conditional rendering

172

function ConditionalFormSpy() {

173

return (

174

<Form onSubmit={(values) => console.log(values)}>

175

{({ handleSubmit }) => (

176

<form onSubmit={handleSubmit}>

177

<Field name="accountType">

178

{({ input }) => (

179

<select {...input}>

180

<option value="">Select Account Type</option>

181

<option value="personal">Personal</option>

182

<option value="business">Business</option>

183

</select>

184

)}

185

</Field>

186

187

<FormSpy subscription={{ values: true }}>

188

{({ values }) => (

189

<>

190

{values.accountType === "business" && (

191

<Field name="companyName" component="input" placeholder="Company Name" />

192

)}

193

{values.accountType === "personal" && (

194

<Field name="dateOfBirth" component="input" type="date" />

195

)}

196

</>

197

)}

198

</FormSpy>

199

</form>

200

)}

201

</Form>

202

);

203

}

204

```

205

206

### FormSpy Subscription Configuration

207

208

FormSpy supports the same subscription configuration as Form components for performance optimization.

209

210

```typescript { .api }

211

/**

212

* FormSpy subscription options for optimized rendering

213

*/

214

interface FormSubscription {

215

active?: boolean;

216

dirty?: boolean;

217

dirtyFields?: boolean;

218

dirtySinceLastSubmit?: boolean;

219

error?: boolean;

220

errors?: boolean;

221

hasSubmitErrors?: boolean;

222

hasValidationErrors?: boolean;

223

initialValues?: boolean;

224

invalid?: boolean;

225

modified?: boolean;

226

modifiedSinceLastSubmit?: boolean;

227

pristine?: boolean;

228

submitError?: boolean;

229

submitErrors?: boolean;

230

submitFailed?: boolean;

231

submitSucceeded?: boolean;

232

submitting?: boolean;

233

touched?: boolean;

234

valid?: boolean;

235

validating?: boolean;

236

values?: boolean;

237

visited?: boolean;

238

}

239

```

240

241

**Usage Example:**

242

243

```typescript

244

// Optimized FormSpy with minimal subscriptions

245

function MinimalFormSpy() {

246

return (

247

<Form onSubmit={(values) => console.log(values)}>

248

{({ handleSubmit }) => (

249

<form onSubmit={handleSubmit}>

250

<Field name="search" component="input" type="search" />

251

252

{/* Only re-renders when values change */}

253

<FormSpy subscription={{ values: true }}>

254

{({ values }) => (

255

<div>

256

Search results for: {values.search}

257

</div>

258

)}

259

</FormSpy>

260

261

{/* Only re-renders when form state changes */}

262

<FormSpy subscription={{ pristine: true, invalid: true, submitting: true }}>

263

{({ pristine, invalid, submitting }) => (

264

<button type="submit" disabled={submitting || pristine || invalid}>

265

{submitting ? "Searching..." : "Search"}

266

</button>

267

)}

268

</FormSpy>

269

</form>

270

)}

271

</Form>

272

);

273

}

274

```

275

276

### External Form State Monitoring

277

278

FormSpy can be used outside of form render functions to monitor form state from parent components.

279

280

**Usage Example:**

281

282

```typescript

283

// External form state monitoring

284

function ExternalMonitor() {

285

const [formState, setFormState] = React.useState<any>(null);

286

287

return (

288

<div>

289

<div>

290

External form monitor:

291

<pre>{JSON.stringify(formState, null, 2)}</pre>

292

</div>

293

294

<Form onSubmit={(values) => console.log(values)}>

295

{({ handleSubmit }) => (

296

<form onSubmit={handleSubmit}>

297

<Field name="data" component="input" />

298

299

<FormSpy onChange={setFormState} />

300

301

<button type="submit">Submit</button>

302

</form>

303

)}

304

</Form>

305

</div>

306

);

307

}

308

```

309

310

### Performance Considerations

311

312

FormSpy is optimized for performance with subscription-based updates and minimal re-rendering.

313

314

```typescript { .api }

315

/**

316

* FormSpy performance characteristics:

317

* - Only re-renders when subscribed form state changes

318

* - Supports granular subscriptions for optimal performance

319

* - Can be used for onChange callbacks without rendering

320

* - Minimal memory footprint with automatic cleanup

321

*/

322

```

323

324

**Usage Example:**

325

326

```typescript

327

// Performance-optimized FormSpy usage

328

function PerformantFormSpy() {

329

return (

330

<Form onSubmit={(values) => console.log(values)}>

331

{({ handleSubmit }) => (

332

<form onSubmit={handleSubmit}>

333

<Field name="title" component="input" />

334

<Field name="content" component="textarea" />

335

336

{/* Separate spies for different concerns */}

337

<FormSpy subscription={{ submitting: true }}>

338

{({ submitting }) => submitting && <div>Saving...</div>}

339

</FormSpy>

340

341

<FormSpy subscription={{ values: true }}>

342

{({ values }) => (

343

<div>Character count: {(values.content || "").length}</div>

344

)}

345

</FormSpy>

346

347

{/* Silent monitoring without render */}

348

<FormSpy

349

onChange={(state) => {

350

// Auto-save draft every 30 seconds

351

if (state.dirty) {

352

console.log("Auto-saving draft...");

353

}

354

}}

355

/>

356

</form>

357

)}

358

</Form>

359

);

360

}

361

```