@graphcommerce/react-hook-form 8.1.0-canary.5 → 8.1.0-canary.50
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 +100 -0
- package/index.ts +1 -0
- package/package.json +8 -4
- package/src/useFormAutoSubmit.tsx +37 -11
- package/src/useFormPersist.tsx +4 -1
- package/src/utils/debounce.ts +16 -0
- package/src/utils/useDebounce.ts +59 -0
- package/src/utils/useDebounceCallback.ts +8 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,105 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 8.1.0-canary.50
|
|
4
|
+
|
|
5
|
+
## 8.1.0-canary.49
|
|
6
|
+
|
|
7
|
+
## 8.1.0-canary.48
|
|
8
|
+
|
|
9
|
+
## 8.1.0-canary.47
|
|
10
|
+
|
|
11
|
+
## 8.1.0-canary.46
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [#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
|
|
16
|
+
([@paales](https://github.com/paales))
|
|
17
|
+
|
|
18
|
+
## 8.1.0-canary.45
|
|
19
|
+
|
|
20
|
+
## 8.1.0-canary.44
|
|
21
|
+
|
|
22
|
+
## 8.1.0-canary.43
|
|
23
|
+
|
|
24
|
+
## 8.1.0-canary.42
|
|
25
|
+
|
|
26
|
+
## 8.1.0-canary.41
|
|
27
|
+
|
|
28
|
+
## 8.1.0-canary.40
|
|
29
|
+
|
|
30
|
+
## 8.1.0-canary.39
|
|
31
|
+
|
|
32
|
+
## 8.1.0-canary.38
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- [#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
|
|
37
|
+
([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
|
|
38
|
+
|
|
39
|
+
## 8.1.0-canary.37
|
|
40
|
+
|
|
41
|
+
## 8.1.0-canary.36
|
|
42
|
+
|
|
43
|
+
## 8.1.0-canary.35
|
|
44
|
+
|
|
45
|
+
## 8.1.0-canary.34
|
|
46
|
+
|
|
47
|
+
## 8.1.0-canary.33
|
|
48
|
+
|
|
49
|
+
## 8.1.0-canary.32
|
|
50
|
+
|
|
51
|
+
## 8.1.0-canary.31
|
|
52
|
+
|
|
53
|
+
## 8.1.0-canary.30
|
|
54
|
+
|
|
55
|
+
## 8.1.0-canary.29
|
|
56
|
+
|
|
57
|
+
## 8.1.0-canary.28
|
|
58
|
+
|
|
59
|
+
## 8.1.0-canary.27
|
|
60
|
+
|
|
61
|
+
## 8.1.0-canary.26
|
|
62
|
+
|
|
63
|
+
## 8.1.0-canary.25
|
|
64
|
+
|
|
65
|
+
## 8.1.0-canary.24
|
|
66
|
+
|
|
67
|
+
## 8.1.0-canary.23
|
|
68
|
+
|
|
69
|
+
## 8.1.0-canary.22
|
|
70
|
+
|
|
71
|
+
## 8.1.0-canary.21
|
|
72
|
+
|
|
73
|
+
## 8.1.0-canary.20
|
|
74
|
+
|
|
75
|
+
## 8.1.0-canary.19
|
|
76
|
+
|
|
77
|
+
## 8.1.0-canary.18
|
|
78
|
+
|
|
79
|
+
## 8.1.0-canary.17
|
|
80
|
+
|
|
81
|
+
## 8.1.0-canary.16
|
|
82
|
+
|
|
83
|
+
## 8.1.0-canary.15
|
|
84
|
+
|
|
85
|
+
## 8.1.0-canary.14
|
|
86
|
+
|
|
87
|
+
## 8.1.0-canary.13
|
|
88
|
+
|
|
89
|
+
## 8.1.0-canary.12
|
|
90
|
+
|
|
91
|
+
## 8.1.0-canary.11
|
|
92
|
+
|
|
93
|
+
## 8.1.0-canary.10
|
|
94
|
+
|
|
95
|
+
## 8.1.0-canary.9
|
|
96
|
+
|
|
97
|
+
## 8.1.0-canary.8
|
|
98
|
+
|
|
99
|
+
## 8.1.0-canary.7
|
|
100
|
+
|
|
101
|
+
## 8.1.0-canary.6
|
|
102
|
+
|
|
3
103
|
## 8.1.0-canary.5
|
|
4
104
|
|
|
5
105
|
## 8.0.6-canary.4
|
package/index.ts
CHANGED
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.
|
|
5
|
+
"version": "8.1.0-canary.50",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
8
|
"eslintConfig": {
|
|
@@ -16,12 +16,16 @@
|
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"@apollo/client": "^3",
|
|
19
|
-
"@graphcommerce/eslint-config-pwa": "^8.1.0-canary.
|
|
20
|
-
"@graphcommerce/prettier-config-pwa": "^8.1.0-canary.
|
|
21
|
-
"@graphcommerce/typescript-config-pwa": "^8.1.0-canary.
|
|
19
|
+
"@graphcommerce/eslint-config-pwa": "^8.1.0-canary.50",
|
|
20
|
+
"@graphcommerce/prettier-config-pwa": "^8.1.0-canary.50",
|
|
21
|
+
"@graphcommerce/typescript-config-pwa": "^8.1.0-canary.50",
|
|
22
22
|
"graphql": "^16.6.0",
|
|
23
23
|
"react": "^18.2.0",
|
|
24
24
|
"react-dom": "^18.2.0",
|
|
25
25
|
"react-hook-form": "^7"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@types/lodash": "^4.17.6",
|
|
29
|
+
"lodash": "^4.17.21"
|
|
26
30
|
}
|
|
27
31
|
}
|
|
@@ -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 {
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
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
|
|
package/src/useFormPersist.tsx
CHANGED
|
@@ -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
|
|
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
|
package/src/utils/debounce.ts
CHANGED
|
@@ -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,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,6 @@
|
|
|
1
1
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
2
|
import useEventCallback from '@mui/utils/useEventCallback'
|
|
3
|
-
import {
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
4
|
import debounce, { DebounceOptions } from './debounce'
|
|
5
5
|
|
|
6
6
|
export function useDebouncedCallback<T extends (...args: any[]) => unknown>(
|
|
@@ -9,13 +9,12 @@ export function useDebouncedCallback<T extends (...args: any[]) => unknown>(
|
|
|
9
9
|
): T {
|
|
10
10
|
const func = useEventCallback(callback)
|
|
11
11
|
|
|
12
|
-
const debounced =
|
|
12
|
+
const debounced = useEventCallback(
|
|
13
|
+
useMemo(
|
|
14
|
+
() => debounce({ func, initialWait, maxWait, wait }),
|
|
15
|
+
[func, initialWait, maxWait, wait],
|
|
16
|
+
),
|
|
17
|
+
)
|
|
13
18
|
|
|
14
|
-
|
|
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
|
|
19
|
+
return debounced
|
|
21
20
|
}
|