@graphcommerce/react-hook-form 9.0.0-canary.98 → 9.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/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": "9.0.0-canary.98",
5
+ "version": "9.0.0",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -11,14 +11,11 @@
11
11
  "project": "./tsconfig.json"
12
12
  }
13
13
  },
14
- "devDependencies": {
15
- "@testing-library/react": "^14.3.1"
16
- },
17
14
  "peerDependencies": {
18
- "@apollo/client": "^3",
19
- "@graphcommerce/eslint-config-pwa": "^9.0.0-canary.98",
20
- "@graphcommerce/prettier-config-pwa": "^9.0.0-canary.98",
21
- "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.98",
15
+ "@apollo/client": "*",
16
+ "@graphcommerce/eslint-config-pwa": "^9.0.0",
17
+ "@graphcommerce/prettier-config-pwa": "^9.0.0",
18
+ "@graphcommerce/typescript-config-pwa": "^9.0.0",
22
19
  "@mui/utils": "^5",
23
20
  "graphql": "^16.6.0",
24
21
  "react": "^18.2.0",
@@ -26,7 +23,7 @@
26
23
  "react-hook-form": "^7"
27
24
  },
28
25
  "dependencies": {
29
- "@types/lodash": "^4.17.7",
26
+ "@types/lodash": "^4.17.13",
30
27
  "lodash": "^4.17.21"
31
28
  }
32
29
  }
@@ -1,9 +1,8 @@
1
1
  import { ApolloError } from '@apollo/client'
2
2
  import React, { useContext, useEffect, useRef } from 'react'
3
- import { GlobalError } from 'react-hook-form'
4
3
  import { isFormGqlOperation } from '../useFormGqlMutation'
5
4
  import { composedFormContext } from './context'
6
- import { ComposedSubmitRenderComponentProps } from './types'
5
+ import type { ComposedSubmitRenderComponentProps } from './types'
7
6
 
8
7
  export type ComposedSubmitProps = {
9
8
  onSubmitSuccessful?: () => void
@@ -1,4 +1,4 @@
1
1
  import React from 'react'
2
- import { ComposedFormContext } from './types'
2
+ import type { ComposedFormContext } from './types'
3
3
 
4
4
  export const composedFormContext = React.createContext(undefined as unknown as ComposedFormContext)
@@ -1,5 +1,5 @@
1
1
  import { isFormGqlOperation } from '../useFormGqlMutation'
2
- import { ComposedFormReducer, ComposedFormState } from './types'
2
+ import type { ComposedFormReducer, ComposedFormState } from './types'
3
3
 
4
4
  function updateFormStateIfNecessary(state: ComposedFormState): ComposedFormState {
5
5
  const formEntries = Object.entries(state.forms)
@@ -1,5 +1,5 @@
1
- import { ApolloError } from '@apollo/client'
2
- import type { FieldValues, FormState, GlobalError, UseFormReturn } from 'react-hook-form'
1
+ import type { ApolloError } from '@apollo/client'
2
+ import type { FieldValues, FormState, 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,8 +1,8 @@
1
1
  import { useContext, useEffect } from 'react'
2
- import { FieldValues, UseFormReturn } from 'react-hook-form'
2
+ import type { FieldValues, UseFormReturn } from 'react-hook-form'
3
3
  import { isFormGqlOperation } from '../useFormGqlMutation'
4
4
  import { composedFormContext } from './context'
5
- import { UseFormComposeOptions } from './types'
5
+ import type { UseFormComposeOptions } from './types'
6
6
 
7
7
  export function useFormCompose<V extends Record<string, unknown>>(
8
8
  fields: UseFormComposeOptions<V>,
@@ -1,20 +1,20 @@
1
1
  /* eslint-disable react/no-unused-prop-types */
2
- import { cloneDeep } from '@apollo/client/utilities'
3
2
  // eslint-disable-next-line import/no-extraneous-dependencies
4
3
  import { useMemoObject } from '@graphcommerce/next-ui/hooks/useMemoObject'
4
+ import { cloneDeep } from '@apollo/client/utilities'
5
5
  // eslint-disable-next-line import/no-extraneous-dependencies
6
6
  import { debounce } from '@mui/material'
7
- import React, { useCallback, useEffect, useRef, useState } from 'react'
8
- import {
7
+ import React, { startTransition, useCallback, useEffect, useRef, useState } from 'react'
8
+ import type {
9
9
  DeepPartialSkipArrayKey,
10
10
  FieldPath,
11
11
  FieldValues,
12
12
  UseFormReturn,
13
13
  UseWatchProps,
14
- useFormState,
15
- useWatch,
16
14
  } from 'react-hook-form'
17
- import { DebounceSettings, useDebounce } from './utils/useDebounce'
15
+ import { useFormState, useWatch } from 'react-hook-form'
16
+ import type { DebounceSettings } from './utils/useDebounce'
17
+ import { useDebounce } from './utils/useDebounce'
18
18
 
19
19
  export type UseFormAutoSubmitOptions<TForm extends UseFormReturn<V>, V extends FieldValues> = {
20
20
  /** Instance of current form */
@@ -48,14 +48,13 @@ export type UseFormAutoSubmitOptions<TForm extends UseFormReturn<V>, V extends F
48
48
  * Q: The form keeps submitting in loops: A: formState.isDirty should be false after submission Make
49
49
  * sure that you call `reset(getValues())` after submission.
50
50
  *
51
+ * @deprecated Please use the <FormAutoSubmit /> component instead. This method causes excessive
52
+ * rerenders.
51
53
  * @see useFormGqlMutation.tsx for an example implementation
52
54
  *
53
55
  * Q: How to I resubmit if the form is modified during the request?
54
56
  * formState.isDirty should be true after the submission
55
57
  * @see useFormGqlMutation.tsx for an example implementation
56
- *
57
- *
58
- * @deprecated Please use the <FormAutoSubmit /> component instead. This method causes excessive rerenders.
59
58
  */
60
59
  export function useFormAutoSubmit<
61
60
  Form extends UseFormReturn<V>,
@@ -128,7 +127,7 @@ export type FormAutoSubmitProps<TFieldValues extends FieldValues = FieldValues>
128
127
  } & Omit<UseWatchProps<TFieldValues>, 'defaultValue'> &
129
128
  DebounceSettings
130
129
 
131
- export function useAutoSubmitBase<TFieldValues extends FieldValues = FieldValues>(
130
+ function useAutoSubmitBase<TFieldValues extends FieldValues = FieldValues>(
132
131
  props: FormAutoSubmitProps<TFieldValues>,
133
132
  ) {
134
133
  const {
@@ -152,8 +151,10 @@ export function useAutoSubmitBase<TFieldValues extends FieldValues = FieldValues
152
151
  const submitDebounced = useDebounce(
153
152
  async () => {
154
153
  try {
155
- oldValues.current = values
156
- await submit()
154
+ await new Promise((resolve) => {
155
+ oldValues.current = values
156
+ resolve(null)
157
+ }).then(() => submit())
157
158
  } catch (e) {
158
159
  // We're not interested if the submission actually succeeds, that should be handled by the form itself.
159
160
  }
@@ -173,7 +174,8 @@ export function useAutoSubmitBase<TFieldValues extends FieldValues = FieldValues
173
174
  }
174
175
 
175
176
  /**
176
- * This is made a components so the useWatch that is used here doesn't retrigger the rerender of the parent component.
177
+ * This is made a components so the useWatch that is used here doesn't retrigger the rerender of the
178
+ * parent component.
177
179
  */
178
180
  function FormAutoSubmitBase<TFieldValues extends FieldValues = FieldValues>(
179
181
  props: FormAutoSubmitProps<TFieldValues>,
@@ -1,30 +1,35 @@
1
- import {
1
+ import type {
2
2
  ApolloError,
3
3
  FetchResult,
4
+ LazyQueryHookOptions,
4
5
  LazyQueryResultTuple,
6
+ MaybeMasked,
7
+ MutationHookOptions,
5
8
  MutationTuple,
6
9
  TypedDocumentNode,
7
- isApolloError,
8
- MutationHookOptions,
9
- LazyQueryHookOptions,
10
10
  } from '@apollo/client'
11
+ import { isApolloError } from '@apollo/client'
11
12
  import { getOperationName } from '@apollo/client/utilities'
12
13
  import useEventCallback from '@mui/utils/useEventCallback'
13
14
  import { useEffect, useRef } from 'react'
14
- import { DefaultValues, FieldValues, UseFormProps, UseFormReturn } from 'react-hook-form'
15
+ import type { DefaultValues, FieldValues, UseFormProps, UseFormReturn } from 'react-hook-form'
15
16
  import diff from './diff'
16
- import { useGqlDocumentHandler, UseGqlDocumentHandler } from './useGqlDocumentHandler'
17
+ import type { UseGqlDocumentHandler } from './useGqlDocumentHandler'
18
+ import { useGqlDocumentHandler } from './useGqlDocumentHandler'
17
19
  import { tryAsync } from './utils/tryTuple'
18
20
 
19
- export type OnCompleteFn<Q, V> = (data: FetchResult<Q>, variables: V) => void | Promise<void>
21
+ export type OnCompleteFn<Q, V> = (
22
+ data: FetchResult<MaybeMasked<Q>>,
23
+ variables: V,
24
+ ) => void | Promise<void>
20
25
 
21
26
  type UseFormGraphQLCallbacks<Q, V> = {
22
27
  /**
23
28
  * Allows you to modify the variablels computed by the form to make it compatible with the GraphQL
24
29
  * Mutation.
25
30
  *
26
- * When returning false, it will silently stop the submission.
27
- * When an error is thrown, it will be set as an ApolloError
31
+ * When returning false, it will silently stop the submission. When an error is thrown, it will be
32
+ * set as an ApolloError
28
33
  */
29
34
  onBeforeSubmit?: (variables: V) => V | false | Promise<V | false>
30
35
  /**
@@ -37,26 +42,27 @@ type UseFormGraphQLCallbacks<Q, V> = {
37
42
  /**
38
43
  * @deprecated Not used anymore, is now the default
39
44
  *
40
- * Changes:
41
- * - Restores `defaultValues` functionality to original functionality, use `values` instead.
42
- * - Does not reset the form after submission, use `values` instead.
43
- * - Does not 'encode' the variables, use onBeforeSubmit instead.
45
+ * Changes:
46
+ *
47
+ * - Restores `defaultValues` functionality to original functionality, use `values` instead.
48
+ * - Does not reset the form after submission, use `values` instead.
49
+ * - Does not 'encode' the variables, use onBeforeSubmit instead.
44
50
  *
45
- * Future plans:
46
- * - Remove the useMutation/useLazyQuery tuple and use a reguler client.mutation() call.
47
- * - Write graphql errors to setError('root')
48
- * - Remove onBeforeSubmit, onComplete and the handleSubmit rewrite with a single mutate() callback.
51
+ * Future plans:
49
52
  *
53
+ * - Remove the useMutation/useLazyQuery tuple and use a reguler client.mutation() call.
54
+ * - Write graphql errors to setError('root')
55
+ * - Remove onBeforeSubmit, onComplete and the handleSubmit rewrite with a single mutate() callback.
50
56
  *
51
- * ```ts
52
- * const { handleSubmit } = useFormGqlMutation();
57
+ * ```ts
58
+ * const { handleSubmit } = useFormGqlMutation()
53
59
  *
54
- * const submit = handleSubmit((formValues, mutate) => {
55
- * // onBeforeSubmit now simply is code before mutate() where you can return early for example or set errors.
56
- * const result = mutate() // executes the mutation and automatically sets generic errors with setError('root')
57
- * // onComplete: now simply use the result after the form, to for example reset the form, or do other things.
58
- * })
59
- * ```
60
+ * const submit = handleSubmit((formValues, mutate) => {
61
+ * // onBeforeSubmit now simply is code before mutate() where you can return early for example or set errors.
62
+ * const result = mutate() // executes the mutation and automatically sets generic errors with setError('root')
63
+ * // onComplete: now simply use the result after the form, to for example reset the form, or do other things.
64
+ * })
65
+ * ```
60
66
  */
61
67
  experimental_useV2?: boolean
62
68
  /**
@@ -67,7 +73,8 @@ type UseFormGraphQLCallbacks<Q, V> = {
67
73
  deprecated_useV1?: boolean
68
74
 
69
75
  /**
70
- * Only submit the form when there are dirty fields. If all fields are clean, we skip the submission.
76
+ * Only submit the form when there are dirty fields. If all fields are clean, we skip the
77
+ * submission.
71
78
  *
72
79
  * Form is still set to isSubmitted and isSubmitSuccessful.
73
80
  */
@@ -82,7 +89,7 @@ export type UseFormGqlMethods<Q, V extends FieldValues> = Omit<
82
89
  'encode' | 'type'
83
90
  > &
84
91
  Pick<UseFormReturn<V>, 'handleSubmit'> & {
85
- data?: Q | null
92
+ data?: MaybeMasked<Q> | null
86
93
  error?: ApolloError
87
94
  submittedVariables?: V
88
95
  }
@@ -154,7 +161,7 @@ export function useFormGql<Q, V extends FieldValues>(
154
161
  : true
155
162
 
156
163
  if (skipUnchanged && !hasDirtyFields) {
157
- console.log(
164
+ console.info(
158
165
  `[useFormGql ${getOperationName(document)}] skipped submission, no dirty fields`,
159
166
  )
160
167
  await onValid(formValues, event)
@@ -173,7 +180,7 @@ export function useFormGql<Q, V extends FieldValues>(
173
180
  if (isApolloError(onBeforeSubmitError)) {
174
181
  returnedError.current = onBeforeSubmitError
175
182
  } else {
176
- console.log(
183
+ console.info(
177
184
  'A non ApolloError was thrown during the onBeforeSubmit handler.',
178
185
  onBeforeSubmitError,
179
186
  )
@@ -209,7 +216,7 @@ export function useFormGql<Q, V extends FieldValues>(
209
216
  if (isApolloError(onCompleteError)) {
210
217
  returnedError.current = onCompleteError
211
218
  } else {
212
- console.log(
219
+ console.info(
213
220
  'A non ApolloError was thrown during the onComplete handler.',
214
221
  onCompleteError,
215
222
  )
@@ -1,8 +1,12 @@
1
- import { MutationHookOptions, TypedDocumentNode, useMutation } from '@apollo/client'
2
- import { FieldValues, useForm, UseFormReturn } from 'react-hook-form'
3
- import { useFormGql, UseFormGqlMethods, UseFormGraphQlOptions } from './useFormGql'
4
- import { useFormMuiRegister, UseMuiFormRegister } from './useFormMuiRegister'
5
- import { UseFormValidReturn } from './useFormValidFields'
1
+ import type { MutationHookOptions, TypedDocumentNode } from '@apollo/client'
2
+ import { useMutation } from '@apollo/client'
3
+ import type { FieldValues, UseFormReturn } from 'react-hook-form'
4
+ import { useForm } from 'react-hook-form'
5
+ import type { UseFormGqlMethods, UseFormGraphQlOptions } from './useFormGql'
6
+ import { useFormGql } from './useFormGql'
7
+ import type { UseMuiFormRegister } from './useFormMuiRegister'
8
+ import { useFormMuiRegister } from './useFormMuiRegister'
9
+ import type { UseFormValidReturn } from './useFormValidFields'
6
10
 
7
11
  export type UseFormGqlMutationReturn<
8
12
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -33,7 +37,7 @@ export function assertFormGqlOperation<
33
37
  Q extends Record<string, unknown> = Record<string, unknown>,
34
38
  >(form: UseFormReturn<V>): asserts form is UseFormGqlMutationReturn<Q, V> {
35
39
  if (typeof (form as UseFormGqlMutationReturn<Q, V>).muiRegister !== 'function') {
36
- throw Error(`form must be one of 'useFromGqlMutation' or 'useFormGqlQuery'`)
40
+ throw Error("form must be one of 'useFromGqlMutation' or 'useFormGqlQuery'")
37
41
  }
38
42
  }
39
43
 
@@ -1,7 +1,10 @@
1
- import { LazyQueryHookOptions, TypedDocumentNode, useLazyQuery } from '@apollo/client'
2
- import { FieldValues, useForm } from 'react-hook-form'
3
- import { useFormGql, UseFormGraphQlOptions } from './useFormGql'
4
- import { UseFormGqlMutationReturn } from './useFormGqlMutation'
1
+ import type { LazyQueryHookOptions, TypedDocumentNode } from '@apollo/client'
2
+ import { useLazyQuery } from '@apollo/client'
3
+ import type { FieldValues } from 'react-hook-form'
4
+ import { useForm } from 'react-hook-form'
5
+ import type { UseFormGraphQlOptions } from './useFormGql'
6
+ import { useFormGql } from './useFormGql'
7
+ import type { UseFormGqlMutationReturn } from './useFormGqlMutation'
5
8
  import { useFormMuiRegister } from './useFormMuiRegister'
6
9
 
7
10
  export type UseFormGqlQueryReturn<
@@ -1,6 +1,6 @@
1
- import {
2
- FieldValues,
1
+ import type {
3
2
  FieldPath,
3
+ FieldValues,
4
4
  RegisterOptions,
5
5
  UseFormRegisterReturn,
6
6
  UseFormReturn,
@@ -1,14 +1,7 @@
1
- import { useMemoObject } from '@graphcommerce/next-ui/hooks/useMemoObject'
1
+ /* eslint-disable react/no-unused-prop-types */
2
2
  import { useEffect } from 'react'
3
- import {
4
- FieldValues,
5
- UseFormReturn,
6
- Path,
7
- FieldPath,
8
- PathValue,
9
- useWatch,
10
- useFormState,
11
- } from 'react-hook-form'
3
+ import type { FieldPath, FieldValues, Path, PathValue, UseFormReturn } from 'react-hook-form'
4
+ import { useFormState, useWatch } from 'react-hook-form'
12
5
 
13
6
  export type UseFormPersistOptions<
14
7
  TFieldValues extends FieldValues = FieldValues,
@@ -40,6 +33,7 @@ export type UseFormPersistOptions<
40
33
  * Todo: Use wath callback so it won't trigger a rerender
41
34
  *
42
35
  * @deprecated Please use the FormPersist component instead. This method causes INP problems.
36
+ * @public
43
37
  */
44
38
  export function useFormPersist<V extends FieldValues>(options: UseFormPersistOptions<V>) {
45
39
  const { form, name, storage = 'sessionStorage', exclude = [], persist = [] } = options
@@ -99,7 +93,8 @@ export function useFormPersist<V extends FieldValues>(options: UseFormPersistOpt
99
93
  }
100
94
 
101
95
  /**
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
96
+ * Please make sure to always include this component at the end of your form because of useWatch
97
+ * rules: https://react-hook-form.com/docs/usewatch
103
98
  */
104
99
  export function FormPersist<V extends FieldValues>(props: UseFormPersistOptions<V>) {
105
100
  useFormPersist(props)
@@ -1,5 +1,5 @@
1
- import { FieldValues, Path, UseFormReturn } from 'react-hook-form'
2
- import { IsRequired } from './useGqlDocumentHandler'
1
+ import type { FieldValues, Path, UseFormReturn } from 'react-hook-form'
2
+ import type { IsRequired } from './useGqlDocumentHandler'
3
3
 
4
4
  export type UseFormValidReturn<TFieldValues> = Partial<Record<Path<TFieldValues>, boolean>>
5
5
 
@@ -9,6 +9,7 @@ export type UseFormValidReturn<TFieldValues> = Partial<Record<Path<TFieldValues>
9
9
  * Record field names as key and boolean as value indicating whether the field is valid
10
10
  *
11
11
  * @deprecated Please use TextInputElement, SelectElement, etc. with the showValid prop
12
+ * @public
12
13
  */
13
14
  export function useFormValidFields<TFieldValues extends FieldValues>(
14
15
  form: Pick<UseFormReturn<TFieldValues>, 'watch' | 'formState'>,
@@ -1,15 +1,16 @@
1
1
  import type { TypedDocumentNode } from '@apollo/client'
2
- import type {
3
- DefinitionNode,
4
- OperationDefinitionNode,
5
- ValueNode,
6
- NullValueNode,
7
- ObjectValueNode,
8
- ListValueNode,
9
- VariableNode,
10
- VariableDefinitionNode,
11
- TypeNode,
12
- OperationTypeNode,
2
+ import {
3
+ type DefinitionNode,
4
+ Kind,
5
+ type ListValueNode,
6
+ type NullValueNode,
7
+ type ObjectValueNode,
8
+ type OperationDefinitionNode,
9
+ type OperationTypeNode,
10
+ type TypeNode,
11
+ type ValueNode,
12
+ type VariableDefinitionNode,
13
+ type VariableNode,
13
14
  } from 'graphql'
14
15
  import { useMemo } from 'react'
15
16
  import type { FieldValues } from 'react-hook-form'
@@ -64,10 +65,10 @@ type DeepStringify<V> = {
64
65
  type FieldTypes = LiteralUnion<keyof Scalars, string> | FieldTypes[]
65
66
 
66
67
  function variableType<T extends TypeNode>(type: T): FieldTypes {
67
- if (type.kind === 'ListType') {
68
+ if (type.kind === Kind.LIST_TYPE) {
68
69
  return [variableType(type.type)]
69
70
  }
70
- if (type.kind === 'NonNullType') {
71
+ if (type.kind === Kind.NON_NULL_TYPE) {
71
72
  return variableType(type.type)
72
73
  }
73
74
 
@@ -103,7 +104,7 @@ export function handlerFactory<Q, V extends FieldValues>(
103
104
  definition.variableDefinitions.forEach((variable: VariableDefinitionNode) => {
104
105
  const name = variable.variable.name.value as keyof V
105
106
 
106
- requiredPartial = { ...requiredPartial, [name]: variable.type.kind === 'NonNullType' }
107
+ requiredPartial = { ...requiredPartial, [name]: variable.type.kind === Kind.NON_NULL_TYPE }
107
108
  encodingPartial = { ...encodingPartial, [name]: variableType(variable.type) }
108
109
 
109
110
  if (variable.defaultValue && isWithValueNode(variable.defaultValue)) {
@@ -1,3 +1,4 @@
1
+ /** @public */
1
2
  export const tryAsync =
2
3
  <R, Args extends unknown[]>(
3
4
  fn: (...args: Args) => Promise<R> | R,
@@ -11,6 +12,7 @@ export const tryAsync =
11
12
  }
12
13
  }
13
14
 
15
+ /** @public */
14
16
  export const trySync =
15
17
  <R, Args extends unknown[]>(
16
18
  fn: (...args: Args) => R,
@@ -1,85 +0,0 @@
1
- export interface Cancelable {
2
- clear(): void
3
- }
4
-
5
- export type DebounceOptions = {
6
- /**
7
- *
8
- * Wait for 200 ms after a request before submitting.
9
- *
10
- * in milliseconds
11
- */
12
- wait?: number
13
- /**
14
- * If there are any pending calls, execute them after this time anyways.
15
- *
16
- * in milliseconds
17
- */
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
- */
24
- initialWait?: number
25
- }
26
-
27
- type DebounceState = {
28
- timeout: ReturnType<typeof setTimeout> | null
29
- firstCallTime: number | null
30
- isFirstCall: boolean
31
- }
32
-
33
- export default function debounce<T extends (...args: unknown[]) => unknown>({
34
- func,
35
- wait = 166,
36
- maxWait = 100000,
37
- initialWait = wait,
38
- }: DebounceOptions & { func: T }): T & Cancelable {
39
- let state: DebounceState = { timeout: null, firstCallTime: null, isFirstCall: true }
40
-
41
- // Guidance for developers - Logging warnings for invalid parameter combinations
42
- if (process.env.NODE_ENV !== 'production') {
43
- // Rewrite to array
44
- const params = { wait, initialWait, maxWait }
45
- const invalidParams = Object.entries(params)
46
- .filter(([, value]) => value < 0)
47
- .map(([key]) => key)
48
-
49
- if (invalidParams.length > 0)
50
- console.warn(`debounce: ${invalidParams.join(', ')} should not be negative.`)
51
-
52
- if (maxWait < wait)
53
- console.warn(`debounce: maxWait should not be less than wait. This does nothing`)
54
- }
55
-
56
- const clear = () => {
57
- if (state.timeout !== null) clearTimeout(state.timeout)
58
- state = { timeout: null, firstCallTime: null, isFirstCall: true }
59
- }
60
-
61
- function debounced<This>(this: This, ...args: Parameters<T>) {
62
- const now = Date.now()
63
- state.firstCallTime ??= now
64
-
65
- const exec = () => {
66
- state.isFirstCall = false
67
- clear()
68
- func.apply(this, args)
69
- }
70
-
71
- const delay = state.isFirstCall ? initialWait : wait
72
-
73
- if (now - state.firstCallTime >= maxWait) {
74
- exec()
75
- return
76
- }
77
-
78
- if (state.timeout !== null) clearTimeout(state.timeout)
79
- state.timeout = setTimeout(exec, delay)
80
- }
81
-
82
- debounced.clear = clear
83
-
84
- return debounced as T & Cancelable
85
- }
@@ -1,20 +0,0 @@
1
- // eslint-disable-next-line import/no-extraneous-dependencies
2
- import useEventCallback from '@mui/utils/useEventCallback'
3
- import { useMemo } from 'react'
4
- import debounce, { DebounceOptions } from './debounce'
5
-
6
- export function useDebouncedCallback<T extends (...args: any[]) => unknown>(
7
- callback: T,
8
- { initialWait, maxWait, wait }: DebounceOptions = {},
9
- ): T {
10
- const func = useEventCallback(callback)
11
-
12
- const debounced = useEventCallback(
13
- useMemo(
14
- () => debounce({ func, initialWait, maxWait, wait }),
15
- [func, initialWait, maxWait, wait],
16
- ),
17
- )
18
-
19
- return debounced
20
- }