@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 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
@@ -8,3 +8,4 @@ export * from './src/useFormValidFields'
8
8
  export * from './src/useFormGql'
9
9
  export * from './src/validationPatterns'
10
10
  export * from './src/ComposedForm'
11
+ export * from './src/utils/useDebounce'
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",
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.5",
20
- "@graphcommerce/prettier-config-pwa": "^8.1.0-canary.5",
21
- "@graphcommerce/typescript-config-pwa": "^8.1.0-canary.5",
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 { 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
 
@@ -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,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 { useEffect, useRef } from 'react'
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 = useRef(debounce({ func, initialWait, maxWait, wait }))
12
+ const debounced = useEventCallback(
13
+ useMemo(
14
+ () => debounce({ func, initialWait, maxWait, wait }),
15
+ [func, initialWait, maxWait, wait],
16
+ ),
17
+ )
13
18
 
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
19
+ return debounced
21
20
  }