@graphcommerce/react-hook-form 8.1.0-canary.9 → 9.0.0-canary.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,215 @@
1
1
  # Change Log
2
2
 
3
+ ## 9.0.0-canary.101
4
+
5
+ ## 9.0.0-canary.100
6
+
7
+ ## 9.0.0-canary.99
8
+
9
+ ## 9.0.0-canary.98
10
+
11
+ ## 9.0.0-canary.97
12
+
13
+ ## 9.0.0-canary.96
14
+
15
+ ## 9.0.0-canary.95
16
+
17
+ ## 9.0.0-canary.94
18
+
19
+ ## 9.0.0-canary.93
20
+
21
+ ## 9.0.0-canary.92
22
+
23
+ ## 9.0.0-canary.91
24
+
25
+ ## 9.0.0-canary.90
26
+
27
+ ### Minor Changes
28
+
29
+ - [#2391](https://github.com/graphcommerce-org/graphcommerce/pull/2391) [`c1fa10f`](https://github.com/graphcommerce-org/graphcommerce/commit/c1fa10f995f562741b7574d465580e5405982a70) - Prevent overwriting custom context in useFormGqlMutation by merging operationOptions before execution. ([@wimvdputten](https://github.com/wimvdputten))
30
+
31
+ ## 9.0.0-canary.89
32
+
33
+ ## 9.0.0-canary.88
34
+
35
+ ## 9.0.0-canary.87
36
+
37
+ ## 9.0.0-canary.86
38
+
39
+ ## 9.0.0-canary.85
40
+
41
+ ## 9.0.0-canary.84
42
+
43
+ ## 9.0.0-canary.83
44
+
45
+ ## 9.0.0-canary.82
46
+
47
+ ## 9.0.0-canary.81
48
+
49
+ ## 9.0.0-canary.80
50
+
51
+ ### Minor Changes
52
+
53
+ - [#2341](https://github.com/graphcommerce-org/graphcommerce/pull/2341) [`16e2980`](https://github.com/graphcommerce-org/graphcommerce/commit/16e2980da4b72330642e59e8c82d1acde387e4fc) - useFormGql and it's derived hooks now have a new `skipUnchanged` prop. The form will only be submitted when there are fields dirty in a form. This reduces the amount of queries ran in the checkout greatly. ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
54
+
55
+ - [#2341](https://github.com/graphcommerce-org/graphcommerce/pull/2341) [`1d6512d`](https://github.com/graphcommerce-org/graphcommerce/commit/1d6512d4118cfb46602aa1f2432c3566fdb3261d) - Rename experimental_useV2 prop to deprecated_useV1 in useFromGql and enable it by default ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
56
+
57
+ ### Patch Changes
58
+
59
+ - [#2341](https://github.com/graphcommerce-org/graphcommerce/pull/2341) [`af45239`](https://github.com/graphcommerce-org/graphcommerce/commit/af452399eaab59ee4e13484fdc9cb0a7660da531) - When a useFormGql throws an error in the onBeforeSubmit method or onComplete method it will setError('root.thrown') with the message, allowing it to be displayed somewhere. PaymentMethodButton will now render this as an ErrorSnackbar. ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
60
+
61
+ ## 9.0.0-canary.79
62
+
63
+ ## 9.0.0-canary.78
64
+
65
+ ## 9.0.0-canary.77
66
+
67
+ ## 9.0.0-canary.76
68
+
69
+ ## 9.0.0-canary.75
70
+
71
+ ## 9.0.0-canary.74
72
+
73
+ ## 9.0.0-canary.73
74
+
75
+ ## 9.0.0-canary.72
76
+
77
+ ## 9.0.0-canary.71
78
+
79
+ ## 9.0.0-canary.70
80
+
81
+ ## 9.0.0-canary.69
82
+
83
+ ## 9.0.0-canary.68
84
+
85
+ ## 9.0.0-canary.67
86
+
87
+ ## 9.0.0-canary.66
88
+
89
+ ## 9.0.0-canary.65
90
+
91
+ ## 9.0.0-canary.64
92
+
93
+ ## 9.0.0-canary.63
94
+
95
+ ## 9.0.0-canary.62
96
+
97
+ ## 9.0.0-canary.61
98
+
99
+ ## 9.0.0-canary.60
100
+
101
+ ## 9.0.0-canary.59
102
+
103
+ ## 9.0.0-canary.58
104
+
105
+ ## 9.0.0-canary.57
106
+
107
+ ## 9.0.0-canary.56
108
+
109
+ ## 9.0.0-canary.55
110
+
111
+ ## 9.0.0-canary.54
112
+
113
+ ## 8.1.0-canary.53
114
+
115
+ ### Minor Changes
116
+
117
+ - [#2325](https://github.com/graphcommerce-org/graphcommerce/pull/2325) [`058fb17`](https://github.com/graphcommerce-org/graphcommerce/commit/058fb1777bdaa51ded6d37529e59a3cc5f0eac06) - Solve an issue where onBeforeSubmit and onComplete would become an 'stale closure' where variables inside wouldn't be updated. By wrapping onBeforeSubmit and onComplete in useEventCallback these functions are updated when outside values get changed. ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
118
+
119
+ ## 8.1.0-canary.52
120
+
121
+ ## 8.1.0-canary.51
122
+
123
+ ## 8.1.0-canary.50
124
+
125
+ ## 8.1.0-canary.49
126
+
127
+ ## 8.1.0-canary.48
128
+
129
+ ## 8.1.0-canary.47
130
+
131
+ ## 8.1.0-canary.46
132
+
133
+ ### Patch Changes
134
+
135
+ - [#2314](https://github.com/graphcommerce-org/graphcommerce/pull/2314) [`490bbfb`](https://github.com/graphcommerce-org/graphcommerce/commit/490bbfb5d88a7f58e83fa9c8b7f475c277a0eda3) - Added missing dependencies of lodash and @types/lodash ([@paales](https://github.com/paales))
136
+
137
+ ## 8.1.0-canary.45
138
+
139
+ ## 8.1.0-canary.44
140
+
141
+ ## 8.1.0-canary.43
142
+
143
+ ## 8.1.0-canary.42
144
+
145
+ ## 8.1.0-canary.41
146
+
147
+ ## 8.1.0-canary.40
148
+
149
+ ## 8.1.0-canary.39
150
+
151
+ ## 8.1.0-canary.38
152
+
153
+ ### Patch Changes
154
+
155
+ - [#2305](https://github.com/graphcommerce-org/graphcommerce/pull/2305) [`77e8297`](https://github.com/graphcommerce-org/graphcommerce/commit/77e82976816994336c616208a651cb18ce9ea270) - Fix bug with persist not applying saved changes by moving <FromPersist/> below the form components ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
156
+
157
+ ## 8.1.0-canary.37
158
+
159
+ ## 8.1.0-canary.36
160
+
161
+ ## 8.1.0-canary.35
162
+
163
+ ## 8.1.0-canary.34
164
+
165
+ ## 8.1.0-canary.33
166
+
167
+ ## 8.1.0-canary.32
168
+
169
+ ## 8.1.0-canary.31
170
+
171
+ ## 8.1.0-canary.30
172
+
173
+ ## 8.1.0-canary.29
174
+
175
+ ## 8.1.0-canary.28
176
+
177
+ ## 8.1.0-canary.27
178
+
179
+ ## 8.1.0-canary.26
180
+
181
+ ## 8.1.0-canary.25
182
+
183
+ ## 8.1.0-canary.24
184
+
185
+ ## 8.1.0-canary.23
186
+
187
+ ## 8.1.0-canary.22
188
+
189
+ ## 8.1.0-canary.21
190
+
191
+ ## 8.1.0-canary.20
192
+
193
+ ## 8.1.0-canary.19
194
+
195
+ ## 8.1.0-canary.18
196
+
197
+ ## 8.1.0-canary.17
198
+
199
+ ## 8.1.0-canary.16
200
+
201
+ ## 8.1.0-canary.15
202
+
203
+ ## 8.1.0-canary.14
204
+
205
+ ## 8.1.0-canary.13
206
+
207
+ ## 8.1.0-canary.12
208
+
209
+ ## 8.1.0-canary.11
210
+
211
+ ## 8.1.0-canary.10
212
+
3
213
  ## 8.1.0-canary.9
4
214
 
5
215
  ## 8.1.0-canary.8
@@ -18,20 +228,15 @@
18
228
 
19
229
  ### Patch Changes
20
230
 
21
- - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`43bd04a`](https://github.com/graphcommerce-org/graphcommerce/commit/43bd04a777c5800cc7e01bee1e123a5aad82f194) - Add deprecation warnings for useFormMuiRegister. Refactor useFormPersist to useWatch and add a separate `<FormPersist/>` component to prevent rerenders.
22
- ([@FrankHarland](https://github.com/FrankHarland))
231
+ - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`43bd04a`](https://github.com/graphcommerce-org/graphcommerce/commit/43bd04a777c5800cc7e01bee1e123a5aad82f194) - Add deprecation warnings for useFormMuiRegister. Refactor useFormPersist to useWatch and add a separate `<FormPersist/>` component to prevent rerenders. ([@FrankHarland](https://github.com/FrankHarland))
23
232
 
24
- - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`0767bc4`](https://github.com/graphcommerce-org/graphcommerce/commit/0767bc40f7b596209f24ca4e745ff0441f3275c9) - Upgrade input components to no longer use muiRegister, which improves INP scores
25
- ([@FrankHarland](https://github.com/FrankHarland))
233
+ - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`0767bc4`](https://github.com/graphcommerce-org/graphcommerce/commit/0767bc40f7b596209f24ca4e745ff0441f3275c9) - Upgrade input components to no longer use muiRegister, which improves INP scores ([@FrankHarland](https://github.com/FrankHarland))
26
234
 
27
- - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`02da217`](https://github.com/graphcommerce-org/graphcommerce/commit/02da2172ef702133510f6923190efae2801032c5) - Migrate most usages of useFormAutoSubmit to <FormAutoSubmit/> and deprecated useFormAutoSubmit
28
- ([@FrankHarland](https://github.com/FrankHarland))
235
+ - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`02da217`](https://github.com/graphcommerce-org/graphcommerce/commit/02da2172ef702133510f6923190efae2801032c5) - Migrate most usages of useFormAutoSubmit to <FormAutoSubmit/> and deprecated useFormAutoSubmit ([@FrankHarland](https://github.com/FrankHarland))
29
236
 
30
- - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`530076e`](https://github.com/graphcommerce-org/graphcommerce/commit/530076e3664703cb8b577b7fcf1998a420819f60) - Moved all usages of useFormPersist to the <FormPersist/> component to prevent rerenders.
31
- ([@FrankHarland](https://github.com/FrankHarland))
237
+ - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`530076e`](https://github.com/graphcommerce-org/graphcommerce/commit/530076e3664703cb8b577b7fcf1998a420819f60) - Moved all usages of useFormPersist to the <FormPersist/> component to prevent rerenders. ([@FrankHarland](https://github.com/FrankHarland))
32
238
 
33
- - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`1a6d0c4`](https://github.com/graphcommerce-org/graphcommerce/commit/1a6d0c4a3584b1e404b444f1ca44c68eaad56cb7) - Mark useFormValidFields as deprecated: Please use TextInputElement, SelectElement, etc. with the showValid prop
34
- ([@FrankHarland](https://github.com/FrankHarland))
239
+ - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`1a6d0c4`](https://github.com/graphcommerce-org/graphcommerce/commit/1a6d0c4a3584b1e404b444f1ca44c68eaad56cb7) - Mark useFormValidFields as deprecated: Please use TextInputElement, SelectElement, etc. with the showValid prop ([@FrankHarland](https://github.com/FrankHarland))
35
240
 
36
241
  ## 8.0.6-canary.1
37
242
 
@@ -41,8 +246,7 @@
41
246
 
42
247
  ### Patch Changes
43
248
 
44
- - [#2237](https://github.com/graphcommerce-org/graphcommerce/pull/2237) [`60f387d`](https://github.com/graphcommerce-org/graphcommerce/commit/60f387d4a037736aa8105fa45728ee481bdaf887) - Solve an issue where the checkout address form wouldn't be automatically submitted on change.
45
- ([@bramvanderholst](https://github.com/bramvanderholst))
249
+ - [#2237](https://github.com/graphcommerce-org/graphcommerce/pull/2237) [`60f387d`](https://github.com/graphcommerce-org/graphcommerce/commit/60f387d4a037736aa8105fa45728ee481bdaf887) - Solve an issue where the checkout address form wouldn't be automatically submitted on change. ([@bramvanderholst](https://github.com/bramvanderholst))
46
250
 
47
251
  ## 8.0.5-canary.10
48
252
 
@@ -60,8 +264,7 @@
60
264
 
61
265
  ### Patch Changes
62
266
 
63
- - [#2237](https://github.com/graphcommerce-org/graphcommerce/pull/2237) [`60f387d`](https://github.com/graphcommerce-org/graphcommerce/commit/60f387d4a037736aa8105fa45728ee481bdaf887) - Solve an issue where the checkout address form wouldn't be automatically submitted on change.
64
- ([@bramvanderholst](https://github.com/bramvanderholst))
267
+ - [#2237](https://github.com/graphcommerce-org/graphcommerce/pull/2237) [`60f387d`](https://github.com/graphcommerce-org/graphcommerce/commit/60f387d4a037736aa8105fa45728ee481bdaf887) - Solve an issue where the checkout address form wouldn't be automatically submitted on change. ([@bramvanderholst](https://github.com/bramvanderholst))
65
268
 
66
269
  ## 8.0.5-canary.3
67
270
 
@@ -81,11 +284,9 @@
81
284
 
82
285
  ### Patch Changes
83
286
 
84
- - [#2206](https://github.com/graphcommerce-org/graphcommerce/pull/2206) [`855ab09`](https://github.com/graphcommerce-org/graphcommerce/commit/855ab097b9ea204a7c73c6550b7a5e9e2290f378) - Cleanup `<FormAutoSubmit/>` and remove internal hook.
85
- ([@paales](https://github.com/paales))
287
+ - [#2206](https://github.com/graphcommerce-org/graphcommerce/pull/2206) [`855ab09`](https://github.com/graphcommerce-org/graphcommerce/commit/855ab097b9ea204a7c73c6550b7a5e9e2290f378) - Cleanup `<FormAutoSubmit/>` and remove internal hook. ([@paales](https://github.com/paales))
86
288
 
87
- - [#2212](https://github.com/graphcommerce-org/graphcommerce/pull/2212) [`7c9f5da`](https://github.com/graphcommerce-org/graphcommerce/commit/7c9f5da1d458a19b0316c556c75415ff28bc5b2d) - Added noValidate prop so we can use the FormAutoSubmit component to submit partial forms
88
- ([@paales](https://github.com/paales))
289
+ - [#2212](https://github.com/graphcommerce-org/graphcommerce/pull/2212) [`7c9f5da`](https://github.com/graphcommerce-org/graphcommerce/commit/7c9f5da1d458a19b0316c556c75415ff28bc5b2d) - Added noValidate prop so we can use the FormAutoSubmit component to submit partial forms ([@paales](https://github.com/paales))
89
290
 
90
291
  ## 8.0.3-canary.6
91
292
 
@@ -93,8 +294,7 @@
93
294
 
94
295
  ### Patch Changes
95
296
 
96
- - [#2212](https://github.com/graphcommerce-org/graphcommerce/pull/2212) [`7c9f5da`](https://github.com/graphcommerce-org/graphcommerce/commit/7c9f5da1d458a19b0316c556c75415ff28bc5b2d) - Added noValidate prop so we can use the FormAutoSubmit component to submit partial forms
97
- ([@paales](https://github.com/paales))
297
+ - [#2212](https://github.com/graphcommerce-org/graphcommerce/pull/2212) [`7c9f5da`](https://github.com/graphcommerce-org/graphcommerce/commit/7c9f5da1d458a19b0316c556c75415ff28bc5b2d) - Added noValidate prop so we can use the FormAutoSubmit component to submit partial forms ([@paales](https://github.com/paales))
98
298
 
99
299
  ## 8.0.3-canary.4
100
300
 
@@ -102,8 +302,7 @@
102
302
 
103
303
  ### Patch Changes
104
304
 
105
- - [#2206](https://github.com/graphcommerce-org/graphcommerce/pull/2206) [`855ab09`](https://github.com/graphcommerce-org/graphcommerce/commit/855ab097b9ea204a7c73c6550b7a5e9e2290f378) - Cleanup `<FormAutoSubmit/>` and remove internal hook.
106
- ([@paales](https://github.com/paales))
305
+ - [#2206](https://github.com/graphcommerce-org/graphcommerce/pull/2206) [`855ab09`](https://github.com/graphcommerce-org/graphcommerce/commit/855ab097b9ea204a7c73c6550b7a5e9e2290f378) - Cleanup `<FormAutoSubmit/>` and remove internal hook. ([@paales](https://github.com/paales))
107
306
 
108
307
  ## 8.0.3-canary.2
109
308
 
@@ -137,11 +336,9 @@
137
336
 
138
337
  ### Patch Changes
139
338
 
140
- - [#2093](https://github.com/graphcommerce-org/graphcommerce/pull/2093) [`112b041`](https://github.com/graphcommerce-org/graphcommerce/commit/112b041f01a33fbd521ce3eb3955844f96b29917) - Created a new experimental mutation abort feature inside `useFormGql`. This will allow redundant mutations to be canceled. This is enabled when the `experimental_useV2` prop on the `useFormGql` hook is used.
141
- ([@mikekeehnen](https://github.com/mikekeehnen))
339
+ - [#2093](https://github.com/graphcommerce-org/graphcommerce/pull/2093) [`112b041`](https://github.com/graphcommerce-org/graphcommerce/commit/112b041f01a33fbd521ce3eb3955844f96b29917) - Created a new experimental mutation abort feature inside `useFormGql`. This will allow redundant mutations to be canceled. This is enabled when the `experimental_useV2` prop on the `useFormGql` hook is used. ([@mikekeehnen](https://github.com/mikekeehnen))
142
340
 
143
- - [#2007](https://github.com/graphcommerce-org/graphcommerce/pull/2007) [`f59c276`](https://github.com/graphcommerce-org/graphcommerce/commit/f59c276605f9ed649d1197a9ba0e3f12d7c6d026) - Crosssell behavior now properly shows for the latest added product. Added latest submitted variables to be retrieved when using the useFormGql hook.
144
- ([@JoshuaS98](https://github.com/JoshuaS98))
341
+ - [#2007](https://github.com/graphcommerce-org/graphcommerce/pull/2007) [`f59c276`](https://github.com/graphcommerce-org/graphcommerce/commit/f59c276605f9ed649d1197a9ba0e3f12d7c6d026) - Crosssell behavior now properly shows for the latest added product. Added latest submitted variables to be retrieved when using the useFormGql hook. ([@JoshuaS98](https://github.com/JoshuaS98))
145
342
 
146
343
  ## 8.0.0-canary.100
147
344
 
package/index.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export * from 'react-hook-form'
2
+ export * from './src/ComposedForm'
3
+ export * from './src/useFormAutoSubmit'
4
+ export * from './src/useFormGql'
2
5
  export * from './src/useFormGqlMutation'
3
6
  export * from './src/useFormGqlQuery'
4
- export * from './src/useFormAutoSubmit'
5
- export * from './src/useFormPersist'
6
7
  export * from './src/useFormMuiRegister'
8
+ export * from './src/useFormPersist'
7
9
  export * from './src/useFormValidFields'
8
- export * from './src/useFormGql'
10
+ export * from './src/utils/tryTuple'
11
+ export * from './src/utils/useDebounce'
9
12
  export * from './src/validationPatterns'
10
- export * from './src/ComposedForm'
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/react-hook-form",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "8.1.0-canary.9",
5
+ "version": "9.0.0-canary.101",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -12,16 +12,21 @@
12
12
  }
13
13
  },
14
14
  "devDependencies": {
15
- "@testing-library/react": "^14.1.2"
15
+ "@testing-library/react": "^14.3.1"
16
16
  },
17
17
  "peerDependencies": {
18
18
  "@apollo/client": "^3",
19
- "@graphcommerce/eslint-config-pwa": "^8.1.0-canary.9",
20
- "@graphcommerce/prettier-config-pwa": "^8.1.0-canary.9",
21
- "@graphcommerce/typescript-config-pwa": "^8.1.0-canary.9",
19
+ "@graphcommerce/eslint-config-pwa": "^9.0.0-canary.101",
20
+ "@graphcommerce/prettier-config-pwa": "^9.0.0-canary.101",
21
+ "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.101",
22
+ "@mui/utils": "^5",
22
23
  "graphql": "^16.6.0",
23
24
  "react": "^18.2.0",
24
25
  "react-dom": "^18.2.0",
25
26
  "react-hook-form": "^7"
27
+ },
28
+ "dependencies": {
29
+ "@types/lodash": "^4.17.10",
30
+ "lodash": "^4.17.21"
26
31
  }
27
32
  }
@@ -1,5 +1,6 @@
1
1
  import { ApolloError } from '@apollo/client'
2
2
  import React, { useContext, useEffect, useRef } from 'react'
3
+ import { GlobalError } from 'react-hook-form'
3
4
  import { isFormGqlOperation } from '../useFormGqlMutation'
4
5
  import { composedFormContext } from './context'
5
6
  import { ComposedSubmitRenderComponentProps } from './types'
@@ -119,6 +120,7 @@ export function ComposedSubmit(props: ComposedSubmitProps) {
119
120
  }
120
121
 
121
122
  const errors: ApolloError[] = []
123
+
122
124
  formEntries.forEach(([, { form }]) => {
123
125
  if (form && isFormGqlOperation(form) && form.error) errors.push(form.error)
124
126
  })
@@ -1,5 +1,5 @@
1
1
  import { ApolloError } from '@apollo/client'
2
- import type { FieldValues, FormState, UseFormReturn } from 'react-hook-form'
2
+ import type { FieldValues, FormState, GlobalError, UseFormReturn } from 'react-hook-form'
3
3
  import type { SetOptional } from 'type-fest'
4
4
 
5
5
  export type UseFormComposeOptions<V extends FieldValues = FieldValues> = {
@@ -1,3 +1,4 @@
1
+ /* eslint-disable react/no-unused-prop-types */
1
2
  import { cloneDeep } from '@apollo/client/utilities'
2
3
  // eslint-disable-next-line import/no-extraneous-dependencies
3
4
  import { useMemoObject } from '@graphcommerce/next-ui/hooks/useMemoObject'
@@ -13,8 +14,7 @@ import {
13
14
  useFormState,
14
15
  useWatch,
15
16
  } from 'react-hook-form'
16
- import { DebounceOptions } from './utils/debounce'
17
- import { useDebouncedCallback } from './utils/useDebounceCallback'
17
+ import { DebounceSettings, useDebounce } from './utils/useDebounce'
18
18
 
19
19
  export type UseFormAutoSubmitOptions<TForm extends UseFormReturn<V>, V extends FieldValues> = {
20
20
  /** Instance of current form */
@@ -116,23 +116,40 @@ export type FormAutoSubmitProps<TFieldValues extends FieldValues = FieldValues>
116
116
  parallel?: boolean
117
117
 
118
118
  noValidate?: boolean
119
- } & DebounceOptions &
120
- Omit<UseWatchProps<TFieldValues>, 'defaultValue'>
121
119
 
122
- /**
123
- * This is made a components so the useWatch that is used here doesn't retrigger the rerender of the parent component.
124
- */
125
- function FormAutoSubmitBase<TFieldValues extends FieldValues = FieldValues>(
120
+ wait?: number
121
+
122
+ /**
123
+ * Only 0 does anthing and will submit immediately. Any other value will be ignored.
124
+ *
125
+ * @deprecated Please use leading instead
126
+ */
127
+ initialWait?: number
128
+ } & Omit<UseWatchProps<TFieldValues>, 'defaultValue'> &
129
+ DebounceSettings
130
+
131
+ export function useAutoSubmitBase<TFieldValues extends FieldValues = FieldValues>(
126
132
  props: FormAutoSubmitProps<TFieldValues>,
127
133
  ) {
128
- const { wait, initialWait, maxWait, submit, parallel, noValidate, ...watchOptions } = props
134
+ const {
135
+ wait = 166,
136
+ initialWait,
137
+ maxWait,
138
+ leading,
139
+ trailing,
140
+
141
+ submit,
142
+ parallel,
143
+ noValidate,
144
+ ...watchOptions
145
+ } = props
129
146
 
130
147
  // We create a stable object from the values, so that we can compare them later
131
148
  const values = useMemoObject(cloneDeep(useWatch(watchOptions)))
132
149
  const oldValues = useRef<DeepPartialSkipArrayKey<TFieldValues>>(values)
133
150
  const { isValidating, isSubmitting, isValid } = useFormState(watchOptions)
134
151
 
135
- const submitDebounced = useDebouncedCallback(
152
+ const submitDebounced = useDebounce(
136
153
  async () => {
137
154
  try {
138
155
  oldValues.current = values
@@ -141,7 +158,8 @@ function FormAutoSubmitBase<TFieldValues extends FieldValues = FieldValues>(
141
158
  // We're not interested if the submission actually succeeds, that should be handled by the form itself.
142
159
  }
143
160
  },
144
- { wait, initialWait, maxWait },
161
+ wait,
162
+ { leading: leading ?? initialWait === 0, maxWait, trailing },
145
163
  )
146
164
 
147
165
  const valid = (noValidate ? true : isValid) && !isValidating
@@ -152,7 +170,15 @@ function FormAutoSubmitBase<TFieldValues extends FieldValues = FieldValues>(
152
170
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
153
171
  submitDebounced()
154
172
  }
173
+ }
155
174
 
175
+ /**
176
+ * This is made a components so the useWatch that is used here doesn't retrigger the rerender of the parent component.
177
+ */
178
+ function FormAutoSubmitBase<TFieldValues extends FieldValues = FieldValues>(
179
+ props: FormAutoSubmitProps<TFieldValues>,
180
+ ) {
181
+ useAutoSubmitBase(props)
156
182
  return null
157
183
  }
158
184
 
@@ -1,26 +1,42 @@
1
1
  import {
2
- FetchResult,
3
- TypedDocumentNode,
4
- MutationTuple,
5
2
  ApolloError,
3
+ FetchResult,
6
4
  LazyQueryResultTuple,
5
+ MutationTuple,
6
+ TypedDocumentNode,
7
+ isApolloError,
8
+ MutationHookOptions,
9
+ LazyQueryHookOptions,
7
10
  } from '@apollo/client'
11
+ import { getOperationName } from '@apollo/client/utilities'
12
+ import useEventCallback from '@mui/utils/useEventCallback'
8
13
  import { useEffect, useRef } from 'react'
9
14
  import { DefaultValues, FieldValues, UseFormProps, UseFormReturn } from 'react-hook-form'
10
15
  import diff from './diff'
11
16
  import { useGqlDocumentHandler, UseGqlDocumentHandler } from './useGqlDocumentHandler'
17
+ import { tryAsync } from './utils/tryTuple'
12
18
 
13
19
  export type OnCompleteFn<Q, V> = (data: FetchResult<Q>, variables: V) => void | Promise<void>
14
20
 
15
21
  type UseFormGraphQLCallbacks<Q, V> = {
16
22
  /**
17
23
  * Allows you to modify the variablels computed by the form to make it compatible with the GraphQL
18
- * Mutation. Also allows you to send false to skip submission.
24
+ * Mutation.
25
+ *
26
+ * When returning false, it will silently stop the submission.
27
+ * When an error is thrown, it will be set as an ApolloError
19
28
  */
20
29
  onBeforeSubmit?: (variables: V) => V | false | Promise<V | false>
30
+ /**
31
+ * Called after the mutation has been executed. Allows you to handle the result of the mutation.
32
+ *
33
+ * When an error is thrown, it will be set as an ApolloError
34
+ */
21
35
  onComplete?: OnCompleteFn<Q, V>
22
36
 
23
37
  /**
38
+ * @deprecated Not used anymore, is now the default
39
+ *
24
40
  * Changes:
25
41
  * - Restores `defaultValues` functionality to original functionality, use `values` instead.
26
42
  * - Does not reset the form after submission, use `values` instead.
@@ -43,6 +59,19 @@ type UseFormGraphQLCallbacks<Q, V> = {
43
59
  * ```
44
60
  */
45
61
  experimental_useV2?: boolean
62
+ /**
63
+ * To restore the previous functionality of the useFormGqlMutation, set this to true.
64
+ *
65
+ * @deprecated Will be removed in the next version.
66
+ */
67
+ deprecated_useV1?: boolean
68
+
69
+ /**
70
+ * Only submit the form when there are dirty fields. If all fields are clean, we skip the submission.
71
+ *
72
+ * Form is still set to isSubmitted and isSubmitSuccessful.
73
+ */
74
+ skipUnchanged?: boolean
46
75
  }
47
76
 
48
77
  export type UseFormGraphQlOptions<Q, V extends FieldValues> = UseFormProps<V> &
@@ -71,7 +100,11 @@ export function useFormGql<Q, V extends FieldValues>(
71
100
  document: TypedDocumentNode<Q, V>
72
101
  form: UseFormReturn<V>
73
102
  tuple: MutationTuple<Q, V> | LazyQueryResultTuple<Q, V>
103
+ operationOptions?:
104
+ | Omit<MutationHookOptions<Q, V>, 'fetchPolicy' | 'variables'>
105
+ | Omit<LazyQueryHookOptions<Q, V>, 'fetchPolicy' | 'variables'>
74
106
  defaultValues?: UseFormProps<V>['defaultValues']
107
+ skipUnchanged?: boolean
75
108
  } & UseFormGraphQLCallbacks<Q, V>,
76
109
  ): UseFormGqlMethods<Q, V> {
77
110
  const {
@@ -80,20 +113,23 @@ export function useFormGql<Q, V extends FieldValues>(
80
113
  document,
81
114
  form,
82
115
  tuple,
116
+ operationOptions,
117
+ skipUnchanged,
83
118
  defaultValues,
84
- experimental_useV2 = false,
119
+ deprecated_useV1 = false,
85
120
  } = options
86
121
  const { encode, type, ...gqlDocumentHandler } = useGqlDocumentHandler<Q, V>(document)
87
122
  const [execute, { data, error, loading }] = tuple
88
123
 
89
124
  const submittedVariables = useRef<V>()
125
+ const returnedError = useRef<ApolloError>()
90
126
 
91
127
  // automatically updates the default values
92
128
  const initital = useRef(true)
93
129
  const controllerRef = useRef<AbortController | undefined>()
94
130
  const valuesString = JSON.stringify(defaultValues)
95
131
  useEffect(() => {
96
- if (experimental_useV2) return
132
+ if (!deprecated_useV1) return
97
133
 
98
134
  if (initital.current) {
99
135
  initital.current = false
@@ -104,32 +140,86 @@ export function useFormGql<Q, V extends FieldValues>(
104
140
  // eslint-disable-next-line react-hooks/exhaustive-deps
105
141
  }, [valuesString, form])
106
142
 
143
+ const beforeSubmit = useEventCallback(
144
+ tryAsync((onBeforeSubmit ?? ((v) => v)) satisfies NonNullable<typeof onBeforeSubmit>),
145
+ )
146
+ const complete = useEventCallback(
147
+ tryAsync((onComplete ?? (() => undefined)) satisfies NonNullable<typeof onComplete>),
148
+ )
149
+
107
150
  const handleSubmit: UseFormReturn<V>['handleSubmit'] = (onValid, onInvalid) =>
108
151
  form.handleSubmit(async (formValues, event) => {
109
- // Combine defaults with the formValues and encode
152
+ const hasDirtyFields = skipUnchanged
153
+ ? Object.values(form?.formState.dirtyFields ?? []).filter(Boolean).length > 0
154
+ : true
155
+
156
+ if (skipUnchanged && !hasDirtyFields) {
157
+ console.log(
158
+ `[useFormGql ${getOperationName(document)}] skipped submission, no dirty fields`,
159
+ )
160
+ await onValid(formValues, event)
161
+ return
162
+ }
163
+
164
+ returnedError.current = undefined
110
165
  submittedVariables.current = undefined
111
- let variables = experimental_useV2 ? formValues : encode({ ...defaultValues, ...formValues })
166
+
167
+ // Combine defaults with the formValues and encode
168
+ let variables = !deprecated_useV1 ? formValues : encode({ ...defaultValues, ...formValues })
112
169
 
113
170
  // Wait for the onBeforeSubmit to complete
114
- if (onBeforeSubmit) {
115
- const res = await onBeforeSubmit(variables)
116
- if (res === false) return
117
- variables = res
171
+ const [onBeforeSubmitResult, onBeforeSubmitError] = await beforeSubmit(variables)
172
+ if (onBeforeSubmitError) {
173
+ if (isApolloError(onBeforeSubmitError)) {
174
+ returnedError.current = onBeforeSubmitError
175
+ } else {
176
+ console.log(
177
+ 'A non ApolloError was thrown during the onBeforeSubmit handler.',
178
+ onBeforeSubmitError,
179
+ )
180
+ }
181
+
182
+ return
118
183
  }
119
- // if (variables === false) onInvalid?.(formValues, event)
184
+ if (onBeforeSubmitResult === false) return
185
+ variables = onBeforeSubmitResult
120
186
 
121
187
  submittedVariables.current = variables
122
- if (loading && experimental_useV2) controllerRef.current?.abort()
188
+ if (!deprecated_useV1 && loading) controllerRef.current?.abort()
123
189
  controllerRef.current = new window.AbortController()
190
+
124
191
  const result = await execute({
192
+ ...operationOptions,
125
193
  variables,
126
- context: { fetchOptions: { signal: controllerRef.current.signal } },
194
+ context: {
195
+ ...operationOptions?.context,
196
+ fetchOptions: {
197
+ ...operationOptions?.context?.fetchOptions,
198
+ signal: controllerRef.current.signal,
199
+ },
200
+ },
127
201
  })
128
202
 
129
- if (onComplete && result.data) await onComplete(result, variables)
203
+ const [, onCompleteError] = await complete(result, variables)
204
+ if (onCompleteError) {
205
+ returnedError.current = onCompleteError as ApolloError
206
+ return
207
+ }
208
+ if (onCompleteError) {
209
+ if (isApolloError(onCompleteError)) {
210
+ returnedError.current = onCompleteError
211
+ } else {
212
+ console.log(
213
+ 'A non ApolloError was thrown during the onComplete handler.',
214
+ onCompleteError,
215
+ )
216
+ }
217
+
218
+ return
219
+ }
130
220
 
131
- // Reset the state of the form if it is unmodified afterwards
132
- if (typeof diff(form.getValues(), formValues) === 'undefined' && !experimental_useV2)
221
+ if (deprecated_useV1 && typeof diff(form.getValues(), formValues) === 'undefined')
222
+ // Reset the state of the form if it is unmodified afterwards
133
223
  form.reset(formValues)
134
224
 
135
225
  await onValid(formValues, event)
@@ -139,7 +229,7 @@ export function useFormGql<Q, V extends FieldValues>(
139
229
  ...gqlDocumentHandler,
140
230
  handleSubmit,
141
231
  data,
142
- error,
232
+ error: error ?? returnedError.current,
143
233
  submittedVariables: submittedVariables.current,
144
234
  }
145
235
  }
@@ -45,7 +45,7 @@ export function useFormGqlMutation<Q extends Record<string, unknown>, V extends
45
45
  ): UseFormGqlMutationReturn<Q, V> {
46
46
  const form = useForm<V>(options)
47
47
  const tuple = useMutation(document, operationOptions)
48
- const operation = useFormGql({ document, form, tuple, ...options })
48
+ const operation = useFormGql({ document, form, tuple, operationOptions, ...options })
49
49
  const muiRegister = useFormMuiRegister(form)
50
50
  return { ...form, ...operation, muiRegister, valid: {} }
51
51
  }
@@ -16,7 +16,7 @@ export function useFormGqlQuery<Q extends Record<string, unknown>, V extends Fie
16
16
  ): UseFormGqlQueryReturn<Q, V> {
17
17
  const form = useForm<V>(options)
18
18
  const tuple = useLazyQuery(document, operationOptions)
19
- const operation = useFormGql({ document, form, tuple, ...options })
19
+ const operation = useFormGql({ document, form, tuple, operationOptions, ...options })
20
20
  const muiRegister = useFormMuiRegister(form)
21
21
 
22
22
  return { ...form, ...operation, valid: {}, muiRegister }
@@ -39,7 +39,7 @@ export type UseFormPersistOptions<
39
39
  *
40
40
  * Todo: Use wath callback so it won't trigger a rerender
41
41
  *
42
- * @deprecated Please use <FormPersist /> instead. This method causes INP problems.
42
+ * @deprecated Please use the FormPersist component instead. This method causes INP problems.
43
43
  */
44
44
  export function useFormPersist<V extends FieldValues>(options: UseFormPersistOptions<V>) {
45
45
  const { form, name, storage = 'sessionStorage', exclude = [], persist = [] } = options
@@ -98,6 +98,9 @@ export function useFormPersist<V extends FieldValues>(options: UseFormPersistOpt
98
98
  }, [name, storage, valuesJson])
99
99
  }
100
100
 
101
+ /**
102
+ * Please make sure to always include this component at the end of your form because of useWatch rules: https://react-hook-form.com/docs/usewatch
103
+ */
101
104
  export function FormPersist<V extends FieldValues>(props: UseFormPersistOptions<V>) {
102
105
  useFormPersist(props)
103
106
  return null
@@ -3,8 +3,24 @@ export interface Cancelable {
3
3
  }
4
4
 
5
5
  export type DebounceOptions = {
6
+ /**
7
+ *
8
+ * Wait for 200 ms after a request before submitting.
9
+ *
10
+ * in milliseconds
11
+ */
6
12
  wait?: number
13
+ /**
14
+ * If there are any pending calls, execute them after this time anyways.
15
+ *
16
+ * in milliseconds
17
+ */
7
18
  maxWait?: number
19
+ /**
20
+ * By default the initialWait is the same as the wait time, but you can set this to a lower value for the initial call.
21
+ *
22
+ * in milliseconds
23
+ */
8
24
  initialWait?: number
9
25
  }
10
26
 
@@ -0,0 +1,25 @@
1
+ export const tryAsync =
2
+ <R, Args extends unknown[]>(
3
+ fn: (...args: Args) => Promise<R> | R,
4
+ ): ((...args: Args) => Promise<[R, undefined] | [undefined, Error]>) =>
5
+ async (...args: Args) => {
6
+ try {
7
+ return [await fn(...args), undefined]
8
+ } catch (e) {
9
+ console.error(e)
10
+ return [undefined, e as Error]
11
+ }
12
+ }
13
+
14
+ export const trySync =
15
+ <R, Args extends unknown[]>(
16
+ fn: (...args: Args) => R,
17
+ ): ((...args: Args) => [R, undefined] | [undefined, Error]) =>
18
+ (...args: Args) => {
19
+ try {
20
+ return [fn(...args), undefined]
21
+ } catch (e) {
22
+ console.error(e)
23
+ return [undefined, e as Error]
24
+ }
25
+ }
@@ -0,0 +1,59 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
2
+ import { useMemoObject } from '@graphcommerce/next-ui/hooks/useMemoObject'
3
+ import useEventCallback from '@mui/utils/useEventCallback'
4
+ import type {
5
+ DebounceSettings,
6
+ DebounceSettingsLeading,
7
+ DebouncedFunc,
8
+ DebouncedFuncLeading,
9
+ } from 'lodash'
10
+ import debounce from 'lodash/debounce'
11
+ import { useMemo } from 'react'
12
+
13
+ export type { DebounceSettings, DebounceSettingsLeading, DebouncedFunc, DebouncedFuncLeading }
14
+
15
+ /**
16
+ * Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since
17
+ * the last time the debounced function was invoked. The debounced function comes with a cancel method to
18
+ * cancel delayed invocations and a flush method to immediately invoke them. Provide an options object to
19
+ * indicate that func should be invoked on the leading and/or trailing edge of the wait timeout. Subsequent
20
+ * calls to the debounced function return the result of the last func invocation.
21
+ *
22
+ * Note: If leading and trailing options are true, func is invoked on the trailing edge of the timeout only
23
+ * if the the debounced function is invoked more than once during the wait timeout.
24
+ *
25
+ * See David Corbacho’s article for details over the differences between _.debounce and _.throttle.
26
+ *
27
+ * @param func The function to debounce.
28
+ * @param wait The number of milliseconds to delay.
29
+ * @param options The options object.
30
+ * @param options.leading Specify invoking on the leading edge of the timeout.
31
+ * @param options.maxWait The maximum time func is allowed to be delayed before it’s invoked.
32
+ * @param options.trailing Specify invoking on the trailing edge of the timeout.
33
+ * @return Returns the new debounced function.
34
+ */
35
+ export function useDebounce<T extends (...args: never[]) => unknown>(
36
+ func: T,
37
+ wait: number | undefined,
38
+ options: DebounceSettingsLeading,
39
+ ): DebouncedFuncLeading<T>
40
+ export function useDebounce<T extends (...args: never[]) => unknown>(
41
+ func: T,
42
+ wait?: number,
43
+ options?: DebounceSettings,
44
+ ): DebouncedFunc<T>
45
+ export function useDebounce<T extends (...args: never[]) => unknown>(
46
+ func: T,
47
+ wait?: number,
48
+ options?: DebounceSettings,
49
+ ): DebouncedFunc<T> {
50
+ const cb = useEventCallback(func)
51
+
52
+ const opts = useMemoObject(
53
+ Object.fromEntries(Object.entries(options ?? {}).filter(([, v]) => v !== undefined)),
54
+ )
55
+
56
+ return useMemo(() => debounce<T>(cb, wait, opts), [cb, opts, wait])
57
+ }
58
+
59
+ export { debounce }
@@ -1,6 +1,5 @@
1
- // eslint-disable-next-line import/no-extraneous-dependencies
2
1
  import useEventCallback from '@mui/utils/useEventCallback'
3
- import { useEffect, useRef } from 'react'
2
+ import { useMemo } from 'react'
4
3
  import debounce, { DebounceOptions } from './debounce'
5
4
 
6
5
  export function useDebouncedCallback<T extends (...args: any[]) => unknown>(
@@ -9,13 +8,12 @@ export function useDebouncedCallback<T extends (...args: any[]) => unknown>(
9
8
  ): T {
10
9
  const func = useEventCallback(callback)
11
10
 
12
- const debounced = useRef(debounce({ func, initialWait, maxWait, wait }))
11
+ const debounced = useEventCallback(
12
+ useMemo(
13
+ () => debounce({ func, initialWait, maxWait, wait }),
14
+ [func, initialWait, maxWait, wait],
15
+ ),
16
+ )
13
17
 
14
- // Re-create the debounced function if the dependencies change
15
- useEffect(() => {
16
- debounced.current = debounce({ func, initialWait, maxWait, wait })
17
- return () => debounced.current.clear()
18
- }, [func, initialWait, maxWait, wait])
19
-
20
- return debounced.current
18
+ return debounced
21
19
  }