@graphcommerce/react-hook-form 7.0.0-canary.13 → 7.0.0
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 +200 -2
- package/package.json +8 -8
- package/src/useFormAutoSubmit.tsx +95 -5
- package/src/useFormPersist.tsx +2 -2
- package/src/utils/debounce.ts +69 -0
- package/src/utils/useDebounceCallback.ts +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,206 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
## 7.0.0
|
|
3
|
+
## 7.0.0
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [`e55d8c390`](https://github.com/graphcommerce-org/graphcommerce/commit/e55d8c390d90b4bb7bab11c6a99027ac72bd7e3e) - Created a new sidebar layout system, can be configured with productFiltersLayout in the graphcommerce.config.js ([@paales](https://github.com/paales))
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#1960](https://github.com/graphcommerce-org/graphcommerce/pull/1960) [`f78caf5a8`](https://github.com/graphcommerce-org/graphcommerce/commit/f78caf5a83683f1ae4b901fb94bd22d50943fa2f) - Updated packages: `next`, `@apollo/client`, `react-hook-form`, `@emotion/*`, `@lingui/*`, `@mui/*` and various others. ([@paales](https://github.com/paales))
|
|
12
|
+
|
|
13
|
+
- [#2012](https://github.com/graphcommerce-org/graphcommerce/pull/2012) [`1dbb3ae13`](https://github.com/graphcommerce-org/graphcommerce/commit/1dbb3ae13553992ee1ed77f375375560f28c418c) - Upgrade graphql to 16.7.1, add graphql as peer dependency ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
|
|
14
|
+
|
|
15
|
+
## 6.2.0-canary.98
|
|
16
|
+
|
|
17
|
+
## 6.2.0-canary.97
|
|
18
|
+
|
|
19
|
+
## 6.2.0-canary.96
|
|
20
|
+
|
|
21
|
+
## 6.2.0-canary.95
|
|
22
|
+
|
|
23
|
+
## 6.2.0-canary.94
|
|
24
|
+
|
|
25
|
+
## 6.2.0-canary.93
|
|
26
|
+
|
|
27
|
+
## 6.2.0-canary.92
|
|
28
|
+
|
|
29
|
+
## 6.2.0-canary.91
|
|
30
|
+
|
|
31
|
+
## 6.2.0-canary.90
|
|
32
|
+
|
|
33
|
+
## 6.2.0-canary.89
|
|
34
|
+
|
|
35
|
+
## 6.2.0-canary.88
|
|
36
|
+
|
|
37
|
+
## 6.2.0-canary.87
|
|
38
|
+
|
|
39
|
+
## 6.2.0-canary.86
|
|
40
|
+
|
|
41
|
+
## 6.2.0-canary.85
|
|
42
|
+
|
|
43
|
+
## 6.2.0-canary.84
|
|
44
|
+
|
|
45
|
+
## 6.2.0-canary.83
|
|
46
|
+
|
|
47
|
+
## 6.2.0-canary.82
|
|
48
|
+
|
|
49
|
+
## 6.2.0-canary.81
|
|
50
|
+
|
|
51
|
+
## 6.2.0-canary.80
|
|
52
|
+
|
|
53
|
+
## 6.2.0-canary.79
|
|
54
|
+
|
|
55
|
+
## 6.2.0-canary.78
|
|
56
|
+
|
|
57
|
+
## 6.2.0-canary.77
|
|
58
|
+
|
|
59
|
+
## 6.2.0-canary.76
|
|
60
|
+
|
|
61
|
+
## 6.2.0-canary.75
|
|
62
|
+
|
|
63
|
+
## 6.2.0-canary.74
|
|
64
|
+
|
|
65
|
+
## 6.2.0-canary.73
|
|
66
|
+
|
|
67
|
+
## 6.2.0-canary.72
|
|
68
|
+
|
|
69
|
+
## 6.2.0-canary.71
|
|
70
|
+
|
|
71
|
+
## 6.2.0-canary.70
|
|
72
|
+
|
|
73
|
+
## 6.2.0-canary.69
|
|
74
|
+
|
|
75
|
+
### Patch Changes
|
|
76
|
+
|
|
77
|
+
- [#2012](https://github.com/graphcommerce-org/graphcommerce/pull/2012) [`1dbb3ae13`](https://github.com/graphcommerce-org/graphcommerce/commit/1dbb3ae13553992ee1ed77f375375560f28c418c) - Upgrade graphql to 16.7.1, add graphql as peer dependency ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
|
|
78
|
+
|
|
79
|
+
## 6.2.0-canary.68
|
|
80
|
+
|
|
81
|
+
## 6.2.0-canary.67
|
|
82
|
+
|
|
83
|
+
### Patch Changes
|
|
84
|
+
|
|
85
|
+
- [#2002](https://github.com/graphcommerce-org/graphcommerce/pull/2002) [`1234bb61f`](https://github.com/graphcommerce-org/graphcommerce/commit/1234bb61f8332da8a9e4dd7262b0c70beaed8c91) - Updated next and apollo/client ([@paales](https://github.com/paales))
|
|
86
|
+
|
|
87
|
+
## 6.2.0-canary.66
|
|
88
|
+
|
|
89
|
+
## 6.2.0-canary.65
|
|
90
|
+
|
|
91
|
+
## 6.2.0-canary.64
|
|
92
|
+
|
|
93
|
+
## 6.2.0-canary.63
|
|
94
|
+
|
|
95
|
+
## 6.2.0-canary.62
|
|
96
|
+
|
|
97
|
+
## 6.2.0-canary.61
|
|
98
|
+
|
|
99
|
+
## 6.2.0-canary.60
|
|
100
|
+
|
|
101
|
+
## 6.2.0-canary.59
|
|
102
|
+
|
|
103
|
+
## 6.2.0-canary.58
|
|
104
|
+
|
|
105
|
+
## 6.2.0-canary.57
|
|
106
|
+
|
|
107
|
+
## 6.2.0-canary.56
|
|
108
|
+
|
|
109
|
+
## 6.2.0-canary.55
|
|
110
|
+
|
|
111
|
+
## 6.2.0-canary.54
|
|
112
|
+
|
|
113
|
+
## 6.2.0-canary.53
|
|
114
|
+
|
|
115
|
+
## 6.2.0-canary.52
|
|
116
|
+
|
|
117
|
+
## 6.2.0-canary.51
|
|
118
|
+
|
|
119
|
+
## 6.2.0-canary.50
|
|
120
|
+
|
|
121
|
+
### Minor Changes
|
|
122
|
+
|
|
123
|
+
- [`e55d8c390`](https://github.com/graphcommerce-org/graphcommerce/commit/e55d8c390d90b4bb7bab11c6a99027ac72bd7e3e) - Created a new sidebar layout system, can be configured with productFiltersLayout in the graphcommerce.config.js ([@paales](https://github.com/paales))
|
|
124
|
+
|
|
125
|
+
## 6.2.0-canary.49
|
|
126
|
+
|
|
127
|
+
## 6.2.0-canary.48
|
|
128
|
+
|
|
129
|
+
## 6.2.0-canary.47
|
|
130
|
+
|
|
131
|
+
## 6.2.0-canary.46
|
|
132
|
+
|
|
133
|
+
## 6.2.0-canary.45
|
|
134
|
+
|
|
135
|
+
## 6.2.0-canary.44
|
|
136
|
+
|
|
137
|
+
## 6.2.0-canary.43
|
|
138
|
+
|
|
139
|
+
## 6.2.0-canary.42
|
|
140
|
+
|
|
141
|
+
## 6.2.0-canary.41
|
|
142
|
+
|
|
143
|
+
### Patch Changes
|
|
144
|
+
|
|
145
|
+
- [#1960](https://github.com/graphcommerce-org/graphcommerce/pull/1960) [`f78caf5a8`](https://github.com/graphcommerce-org/graphcommerce/commit/f78caf5a83683f1ae4b901fb94bd22d50943fa2f) - Updated packages @apollo/client, react-hook-form, @emotion/\*, @lingui/\*, @mui/\* and various others. ([@paales](https://github.com/paales))
|
|
146
|
+
|
|
147
|
+
## 6.2.0-canary.40
|
|
148
|
+
|
|
149
|
+
## 6.2.0-canary.39
|
|
150
|
+
|
|
151
|
+
## 6.2.0-canary.38
|
|
152
|
+
|
|
153
|
+
## 6.2.0-canary.37
|
|
154
|
+
|
|
155
|
+
## 6.2.0-canary.36
|
|
156
|
+
|
|
157
|
+
## 6.2.0-canary.35
|
|
158
|
+
|
|
159
|
+
## 6.2.0-canary.34
|
|
160
|
+
|
|
161
|
+
## 6.2.0-canary.33
|
|
162
|
+
|
|
163
|
+
## 6.2.0-canary.32
|
|
164
|
+
|
|
165
|
+
## 6.2.0-canary.31
|
|
166
|
+
|
|
167
|
+
## 6.2.0-canary.30
|
|
168
|
+
|
|
169
|
+
## 6.2.0-canary.29
|
|
170
|
+
|
|
171
|
+
## 6.2.0-canary.28
|
|
172
|
+
|
|
173
|
+
## 6.2.0-canary.27
|
|
174
|
+
|
|
175
|
+
## 6.2.0-canary.26
|
|
176
|
+
|
|
177
|
+
## 6.2.0-canary.25
|
|
178
|
+
|
|
179
|
+
## 6.2.0-canary.24
|
|
180
|
+
|
|
181
|
+
## 6.2.0-canary.23
|
|
182
|
+
|
|
183
|
+
## 6.2.0-canary.22
|
|
184
|
+
|
|
185
|
+
## 6.2.0-canary.21
|
|
186
|
+
|
|
187
|
+
## 6.2.0-canary.20
|
|
188
|
+
|
|
189
|
+
## 6.2.0-canary.19
|
|
190
|
+
|
|
191
|
+
## 6.2.0-canary.18
|
|
192
|
+
|
|
193
|
+
## 6.2.0-canary.17
|
|
194
|
+
|
|
195
|
+
## 6.2.0-canary.16
|
|
196
|
+
|
|
197
|
+
## 6.2.0-canary.15
|
|
198
|
+
|
|
199
|
+
## 6.2.0-canary.14
|
|
200
|
+
|
|
201
|
+
## 6.2.0-canary.13
|
|
202
|
+
|
|
203
|
+
## 6.2.0-canary.12
|
|
6
204
|
|
|
7
205
|
## 6.2.0-canary.11
|
|
8
206
|
|
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": "7.0.0
|
|
5
|
+
"version": "7.0.0",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
8
|
"eslintConfig": {
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@apollo/client": "
|
|
16
|
-
"
|
|
17
|
-
"react-hook-form": "7.43.5"
|
|
15
|
+
"@apollo/client": "~3.7.17",
|
|
16
|
+
"react-hook-form": "7.44.3"
|
|
18
17
|
},
|
|
19
18
|
"devDependencies": {
|
|
20
|
-
"@graphcommerce/eslint-config-pwa": "7.0.0
|
|
21
|
-
"@graphcommerce/prettier-config-pwa": "7.0.0
|
|
22
|
-
"@graphcommerce/typescript-config-pwa": "7.0.0
|
|
19
|
+
"@graphcommerce/eslint-config-pwa": "7.0.0",
|
|
20
|
+
"@graphcommerce/prettier-config-pwa": "7.0.0",
|
|
21
|
+
"@graphcommerce/typescript-config-pwa": "7.0.0",
|
|
23
22
|
"@testing-library/react": "^14.0.0"
|
|
24
23
|
},
|
|
25
24
|
"peerDependencies": {
|
|
26
25
|
"react": "^18.2.0",
|
|
27
|
-
"react-dom": "^18.2.0"
|
|
26
|
+
"react-dom": "^18.2.0",
|
|
27
|
+
"graphql": "^16.6.0"
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
+
import { cloneDeep } from '@apollo/client/utilities'
|
|
2
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
3
|
+
import { useMemoObject } from '@graphcommerce/next-ui/hooks/useMemoObject'
|
|
1
4
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
5
|
import { debounce } from '@mui/material'
|
|
3
|
-
import { useCallback, useEffect, useState } from 'react'
|
|
4
|
-
import {
|
|
6
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|
7
|
+
import {
|
|
8
|
+
Control,
|
|
9
|
+
DeepPartialSkipArrayKey,
|
|
10
|
+
FieldPath,
|
|
11
|
+
FieldValues,
|
|
12
|
+
UseFormReturn,
|
|
13
|
+
useFormState,
|
|
14
|
+
useWatch,
|
|
15
|
+
} from 'react-hook-form'
|
|
16
|
+
import { DebounceOptions } from './utils/debounce'
|
|
17
|
+
import { useDebouncedCallback } from './utils/useDebounceCallback'
|
|
5
18
|
|
|
6
19
|
export type UseFormAutoSubmitOptions<TForm extends UseFormReturn<V>, V extends FieldValues> = {
|
|
7
20
|
/** Instance of current form */
|
|
@@ -18,6 +31,8 @@ export type UseFormAutoSubmitOptions<TForm extends UseFormReturn<V>, V extends F
|
|
|
18
31
|
* that this may cause extra requests
|
|
19
32
|
*/
|
|
20
33
|
forceInitialSubmit?: boolean
|
|
34
|
+
/** Disables the hook */
|
|
35
|
+
disabled?: boolean
|
|
21
36
|
}
|
|
22
37
|
|
|
23
38
|
/**
|
|
@@ -43,7 +58,7 @@ export function useFormAutoSubmit<
|
|
|
43
58
|
Form extends UseFormReturn<V>,
|
|
44
59
|
V extends FieldValues = FieldValues,
|
|
45
60
|
>(options: UseFormAutoSubmitOptions<Form, V>) {
|
|
46
|
-
const { form, submit, wait = 500, fields, forceInitialSubmit } = options
|
|
61
|
+
const { form, submit, wait = 500, fields, forceInitialSubmit, disabled } = options
|
|
47
62
|
const { formState } = form
|
|
48
63
|
|
|
49
64
|
const [submitting, setSubmitting] = useState(false)
|
|
@@ -72,13 +87,88 @@ export function useFormAutoSubmit<
|
|
|
72
87
|
)
|
|
73
88
|
|
|
74
89
|
useEffect(() => {
|
|
75
|
-
if (canSubmit && (force || shouldSubmit)) {
|
|
90
|
+
if (!disabled && canSubmit && (force || shouldSubmit)) {
|
|
76
91
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
77
92
|
submitDebounced()
|
|
78
93
|
return () => submitDebounced.clear()
|
|
79
94
|
}
|
|
80
95
|
return () => {}
|
|
81
|
-
}, [canSubmit, force, shouldSubmit, submitDebounced])
|
|
96
|
+
}, [canSubmit, force, shouldSubmit, submitDebounced, disabled])
|
|
82
97
|
|
|
83
98
|
return submitting
|
|
84
99
|
}
|
|
100
|
+
|
|
101
|
+
export type FormAutoSubmitProps<
|
|
102
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
103
|
+
TFieldNames extends readonly FieldPath<TFieldValues>[] = readonly FieldPath<TFieldValues>[],
|
|
104
|
+
> = {
|
|
105
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
106
|
+
control: Control<TFieldValues>
|
|
107
|
+
/** Autosubmit only when these field names update */
|
|
108
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
109
|
+
name?: readonly [...TFieldNames]
|
|
110
|
+
|
|
111
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
112
|
+
disabled?: boolean
|
|
113
|
+
|
|
114
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
115
|
+
exact?: boolean
|
|
116
|
+
|
|
117
|
+
/** SubmitHandler */
|
|
118
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
119
|
+
submit: ReturnType<UseFormReturn<TFieldValues>['handleSubmit']>
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* When a current submission is already in flight, should we wait for it to finish before
|
|
123
|
+
* submitting again?
|
|
124
|
+
*/
|
|
125
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
126
|
+
parallel?: boolean
|
|
127
|
+
} & DebounceOptions
|
|
128
|
+
|
|
129
|
+
function useFormAutoSubmit2<
|
|
130
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
131
|
+
TFieldNames extends readonly FieldPath<TFieldValues>[] = readonly FieldPath<TFieldValues>[],
|
|
132
|
+
>(props: FormAutoSubmitProps<TFieldValues, TFieldNames>) {
|
|
133
|
+
const { wait, initialWait, maxWait, submit, parallel, ...watchOptions } = props
|
|
134
|
+
|
|
135
|
+
// We create a stable object from the values, so that we can compare them later
|
|
136
|
+
const values = useMemoObject(cloneDeep(useWatch(watchOptions)))
|
|
137
|
+
const oldValues = useRef<DeepPartialSkipArrayKey<TFieldValues>>(values)
|
|
138
|
+
const { isValidating, isSubmitting, isValid } = useFormState(watchOptions)
|
|
139
|
+
|
|
140
|
+
const submitDebounced = useDebouncedCallback(
|
|
141
|
+
async () => {
|
|
142
|
+
try {
|
|
143
|
+
oldValues.current = values
|
|
144
|
+
await submit()
|
|
145
|
+
} catch (e) {
|
|
146
|
+
// We're not interested if the submission actually succeeds, that should be handled by the form itself.
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
{ wait, initialWait, maxWait },
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
const valid = isValid && !isValidating
|
|
153
|
+
const allowed = parallel || !isSubmitting
|
|
154
|
+
const canSubmit = valid && allowed
|
|
155
|
+
|
|
156
|
+
if (canSubmit && values !== oldValues.current) {
|
|
157
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
158
|
+
submitDebounced()
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* We're wrapping this in a component so that the parent component doesn't rerender on every
|
|
164
|
+
* submission.
|
|
165
|
+
*/
|
|
166
|
+
function FormAutoSubmitBase<
|
|
167
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
168
|
+
TFieldNames extends readonly FieldPath<TFieldValues>[] = readonly FieldPath<TFieldValues>[],
|
|
169
|
+
>(props: FormAutoSubmitProps<TFieldValues, TFieldNames>) {
|
|
170
|
+
useFormAutoSubmit2(props)
|
|
171
|
+
return null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export const FormAutoSubmit = React.memo(FormAutoSubmitBase) as typeof FormAutoSubmitBase
|
package/src/useFormPersist.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect } from 'react'
|
|
2
|
-
import { FieldValues, UseFormReturn, Path,
|
|
2
|
+
import { FieldValues, UseFormReturn, Path, FieldPath, PathValue } from 'react-hook-form'
|
|
3
3
|
|
|
4
4
|
export type UseFormPersistOptions<
|
|
5
5
|
TFieldValues extends FieldValues = FieldValues,
|
|
@@ -58,7 +58,7 @@ export function useFormPersist<V extends FieldValues>(options: UseFormPersistOpt
|
|
|
58
58
|
|
|
59
59
|
const storedValues = JSON.parse(storedFormStr) as FieldValues
|
|
60
60
|
if (storedValues) {
|
|
61
|
-
const entries = Object.entries(storedValues) as [Path<V>,
|
|
61
|
+
const entries = Object.entries(storedValues) as [Path<V>, PathValue<V, Path<V>>][]
|
|
62
62
|
entries.forEach(([entryName, value]) =>
|
|
63
63
|
setValue(entryName, value, {
|
|
64
64
|
shouldDirty: true,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export interface Cancelable {
|
|
2
|
+
clear(): void
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export type DebounceOptions = {
|
|
6
|
+
wait?: number
|
|
7
|
+
maxWait?: number
|
|
8
|
+
initialWait?: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type DebounceState = {
|
|
12
|
+
timeout: ReturnType<typeof setTimeout> | null
|
|
13
|
+
firstCallTime: number | null
|
|
14
|
+
isFirstCall: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function debounce<T extends (...args: unknown[]) => unknown>({
|
|
18
|
+
func,
|
|
19
|
+
wait = 166,
|
|
20
|
+
maxWait = 100000,
|
|
21
|
+
initialWait = wait,
|
|
22
|
+
}: DebounceOptions & { func: T }): T & Cancelable {
|
|
23
|
+
let state: DebounceState = { timeout: null, firstCallTime: null, isFirstCall: true }
|
|
24
|
+
|
|
25
|
+
// Guidance for developers - Logging warnings for invalid parameter combinations
|
|
26
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
27
|
+
// Rewrite to array
|
|
28
|
+
const params = { wait, initialWait, maxWait }
|
|
29
|
+
const invalidParams = Object.entries(params)
|
|
30
|
+
.filter(([, value]) => value < 0)
|
|
31
|
+
.map(([key]) => key)
|
|
32
|
+
|
|
33
|
+
if (invalidParams.length > 0)
|
|
34
|
+
console.warn(`debounce: ${invalidParams.join(', ')} should not be negative.`)
|
|
35
|
+
|
|
36
|
+
if (maxWait < wait)
|
|
37
|
+
console.warn(`debounce: maxWait should not be less than wait. This does nothing`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const clear = () => {
|
|
41
|
+
if (state.timeout !== null) clearTimeout(state.timeout)
|
|
42
|
+
state = { timeout: null, firstCallTime: null, isFirstCall: true }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function debounced<This>(this: This, ...args: Parameters<T>) {
|
|
46
|
+
const now = Date.now()
|
|
47
|
+
state.firstCallTime ??= now
|
|
48
|
+
|
|
49
|
+
const exec = () => {
|
|
50
|
+
state.isFirstCall = false
|
|
51
|
+
clear()
|
|
52
|
+
func.apply(this, args)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const delay = state.isFirstCall ? initialWait : wait
|
|
56
|
+
|
|
57
|
+
if (now - state.firstCallTime >= maxWait) {
|
|
58
|
+
exec()
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (state.timeout !== null) clearTimeout(state.timeout)
|
|
63
|
+
state.timeout = setTimeout(exec, delay)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
debounced.clear = clear
|
|
67
|
+
|
|
68
|
+
return debounced as T & Cancelable
|
|
69
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
+
import useEventCallback from '@mui/utils/useEventCallback'
|
|
3
|
+
import { useEffect, useRef } from 'react'
|
|
4
|
+
import debounce, { DebounceOptions } from './debounce'
|
|
5
|
+
|
|
6
|
+
export function useDebouncedCallback<T extends (...args: unknown[]) => unknown>(
|
|
7
|
+
callback: T,
|
|
8
|
+
{ initialWait, maxWait, wait }: DebounceOptions = {},
|
|
9
|
+
): T {
|
|
10
|
+
const func = useEventCallback(callback)
|
|
11
|
+
|
|
12
|
+
const debounced = useRef(debounce({ func, initialWait, maxWait, wait }))
|
|
13
|
+
|
|
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
|
|
21
|
+
}
|