remix-validated-form 4.1.0-beta.2 → 4.1.2

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.
Files changed (57) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/browser/ValidatedForm.d.ts +1 -1
  3. package/browser/ValidatedForm.js +9 -3
  4. package/browser/internal/MultiValueMap.js +3 -3
  5. package/browser/internal/hooks-valtio.d.ts +18 -0
  6. package/browser/internal/hooks-valtio.js +110 -0
  7. package/browser/internal/hooks-zustand.d.ts +16 -0
  8. package/browser/internal/hooks-zustand.js +100 -0
  9. package/browser/internal/hydratable.d.ts +14 -0
  10. package/browser/internal/hydratable.js +14 -0
  11. package/browser/internal/immerMiddleware.d.ts +6 -0
  12. package/browser/internal/immerMiddleware.js +7 -0
  13. package/browser/internal/logic/getCheckboxChecked copy.d.ts +1 -0
  14. package/browser/internal/logic/getCheckboxChecked copy.js +9 -0
  15. package/browser/internal/logic/getCheckboxChecked.d.ts +1 -0
  16. package/browser/internal/logic/getCheckboxChecked.js +9 -0
  17. package/browser/internal/logic/getRadioChecked.d.ts +1 -0
  18. package/browser/internal/logic/getRadioChecked.js +5 -0
  19. package/browser/internal/logic/setFieldValue.d.ts +1 -0
  20. package/browser/internal/logic/setFieldValue.js +40 -0
  21. package/browser/internal/logic/setInputValueInForm.d.ts +1 -0
  22. package/browser/internal/logic/setInputValueInForm.js +40 -0
  23. package/browser/internal/state-valtio.d.ts +62 -0
  24. package/browser/internal/state-valtio.js +69 -0
  25. package/browser/internal/state-zustand.d.ts +47 -0
  26. package/browser/internal/state-zustand.js +85 -0
  27. package/browser/internal/util.d.ts +1 -0
  28. package/browser/internal/util.js +12 -1
  29. package/build/ValidatedForm.d.ts +1 -1
  30. package/build/ValidatedForm.js +8 -2
  31. package/build/internal/MultiValueMap.js +2 -2
  32. package/build/internal/hooks-valtio.d.ts +18 -0
  33. package/build/internal/hooks-valtio.js +128 -0
  34. package/build/internal/hooks-zustand.d.ts +16 -0
  35. package/build/internal/hooks-zustand.js +117 -0
  36. package/build/internal/hydratable.d.ts +14 -0
  37. package/build/internal/hydratable.js +17 -0
  38. package/build/internal/immerMiddleware.d.ts +6 -0
  39. package/build/internal/immerMiddleware.js +14 -0
  40. package/build/internal/logic/getCheckboxChecked.d.ts +1 -0
  41. package/build/internal/logic/getCheckboxChecked.js +13 -0
  42. package/build/internal/logic/getRadioChecked.d.ts +1 -0
  43. package/build/internal/logic/getRadioChecked.js +9 -0
  44. package/build/internal/logic/setFieldValue.d.ts +1 -0
  45. package/build/internal/logic/setFieldValue.js +47 -0
  46. package/build/internal/logic/setInputValueInForm.d.ts +1 -0
  47. package/build/internal/logic/setInputValueInForm.js +47 -0
  48. package/build/internal/state-valtio.d.ts +62 -0
  49. package/build/internal/state-valtio.js +83 -0
  50. package/build/internal/state-zustand.d.ts +47 -0
  51. package/build/internal/state-zustand.js +91 -0
  52. package/build/internal/util.d.ts +1 -0
  53. package/build/internal/util.js +16 -1
  54. package/package.json +1 -1
  55. package/src/ValidatedForm.tsx +10 -2
  56. package/src/internal/MultiValueMap.ts +3 -3
  57. package/src/internal/util.ts +13 -1
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.setInputValueInForm = void 0;
7
+ const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
8
+ const getCheckboxChecked_1 = require("./getCheckboxChecked");
9
+ const getRadioChecked_1 = require("./getRadioChecked");
10
+ const setElementValue = (element, value, name) => {
11
+ if (element instanceof HTMLSelectElement && element.multiple) {
12
+ (0, tiny_invariant_1.default)(Array.isArray(value), "Must specify an array to set the value for a multi-select");
13
+ for (const option of element.options) {
14
+ option.selected = value.includes(option.value);
15
+ }
16
+ return;
17
+ }
18
+ if (element instanceof HTMLInputElement && element.type === "checkbox") {
19
+ const newChecked = (0, getCheckboxChecked_1.getCheckboxChecked)(element.value, value);
20
+ (0, tiny_invariant_1.default)(newChecked !== undefined, `Unable to determine if checkbox should be checked. Provided value was ${value} for checkbox ${name}.`);
21
+ element.checked = newChecked;
22
+ return;
23
+ }
24
+ if (element instanceof HTMLInputElement && element.type === "radio") {
25
+ const newChecked = (0, getRadioChecked_1.getRadioChecked)(element.value, value);
26
+ (0, tiny_invariant_1.default)(newChecked !== undefined, `Unable to determine if radio should be checked. Provided value was ${value} for radio ${name}.`);
27
+ element.checked = newChecked;
28
+ return;
29
+ }
30
+ (0, tiny_invariant_1.default)(typeof value === "string", `Invalid value for field "${name}" which is an ${element.constructor.name}. Expected string but received ${typeof value}`);
31
+ const input = element;
32
+ input.value = value;
33
+ };
34
+ const setInputValueInForm = (formElement, name, value) => {
35
+ const controlElement = formElement.elements.namedItem(name);
36
+ if (!controlElement)
37
+ return;
38
+ if (controlElement instanceof RadioNodeList) {
39
+ for (const element of controlElement) {
40
+ setElementValue(element, value, name);
41
+ }
42
+ }
43
+ else {
44
+ setElementValue(controlElement, value, name);
45
+ }
46
+ };
47
+ exports.setInputValueInForm = setInputValueInForm;
@@ -0,0 +1,62 @@
1
+ import { FieldErrors, TouchedFields } from "..";
2
+ export declare type InternalFormState = {
3
+ hydrated: boolean;
4
+ fieldErrors: FieldErrors;
5
+ isSubmitting: boolean;
6
+ hasBeenSubmitted: boolean;
7
+ touchedFields: TouchedFields;
8
+ action?: string;
9
+ subaction?: string;
10
+ defaultValues: {
11
+ [fieldName: string]: any;
12
+ };
13
+ validateField: (fieldName: string) => Promise<string | null>;
14
+ registerReceiveFocus: (fieldName: string, handler: () => void) => () => void;
15
+ setFieldValue: (fieldName: string, value: unknown) => void;
16
+ };
17
+ declare type SyncFormArgs = {
18
+ defaultValues?: {
19
+ [fieldName: string]: any;
20
+ };
21
+ action?: string;
22
+ subaction?: string;
23
+ validateField: InternalFormState["validateField"];
24
+ registerReceiveFocus: InternalFormState["registerReceiveFocus"];
25
+ setFieldValueForForm: InternalFormState["setFieldValue"];
26
+ };
27
+ declare type StoreState = {
28
+ forms: {
29
+ [formId: string | symbol]: InternalFormState;
30
+ };
31
+ };
32
+ export declare const state: StoreState;
33
+ export declare const registerFormSlice: (formId: string | symbol, { registerReceiveFocus, setFieldValueForForm, validateField, action, defaultValues, subaction, }: SyncFormArgs) => void;
34
+ export declare const unregisterFormSlice: (formId: string | symbol) => void;
35
+ export declare const useFormData: (formId: string | symbol) => {
36
+ readonly hydrated: boolean;
37
+ readonly fieldErrors: {
38
+ readonly [x: string]: string;
39
+ };
40
+ readonly isSubmitting: boolean;
41
+ readonly hasBeenSubmitted: boolean;
42
+ readonly touchedFields: {
43
+ readonly [x: string]: boolean;
44
+ };
45
+ readonly action?: string | undefined;
46
+ readonly subaction?: string | undefined;
47
+ readonly defaultValues: {
48
+ readonly [x: string]: any;
49
+ };
50
+ readonly validateField: (fieldName: string) => Promise<string | null>;
51
+ readonly registerReceiveFocus: (fieldName: string, handler: () => void) => () => void;
52
+ readonly setFieldValue: (fieldName: string, value: unknown) => void;
53
+ };
54
+ export declare const startSubmit: (formId: string | symbol) => void;
55
+ export declare const endSubmit: (formId: string | symbol) => void;
56
+ export declare const sync: (formId: string | symbol, { defaultValues, action, subaction, registerReceiveFocus, validateField, setFieldValueForForm, }: SyncFormArgs) => void;
57
+ export declare const clearError: (formId: string | symbol, fieldName: string) => void;
58
+ export declare const addError: (formId: string | symbol, fieldName: string, error: string) => void;
59
+ export declare const setTouched: (formId: string | symbol, fieldName: string, touched: boolean) => void;
60
+ export declare const reset: (formId: string | symbol) => void;
61
+ export declare const setFieldErrors: (formId: string | symbol, fieldErrors: FieldErrors) => void;
62
+ export {};
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setFieldErrors = exports.reset = exports.setTouched = exports.addError = exports.clearError = exports.sync = exports.endSubmit = exports.startSubmit = exports.useFormData = exports.unregisterFormSlice = exports.registerFormSlice = exports.state = void 0;
4
+ const valtio_1 = require("valtio");
5
+ exports.state = (0, valtio_1.proxy)({ forms: {} });
6
+ const registerFormSlice = (formId, { registerReceiveFocus, setFieldValueForForm, validateField, action, defaultValues, subaction, }) => {
7
+ exports.state.forms[formId] = {
8
+ hydrated: true,
9
+ defaultValues: defaultValues !== null && defaultValues !== void 0 ? defaultValues : {},
10
+ fieldErrors: {},
11
+ hasBeenSubmitted: false,
12
+ isSubmitting: false,
13
+ touchedFields: {},
14
+ registerReceiveFocus,
15
+ setFieldValue: setFieldValueForForm,
16
+ validateField,
17
+ action,
18
+ subaction,
19
+ };
20
+ };
21
+ exports.registerFormSlice = registerFormSlice;
22
+ const unregisterFormSlice = (formId) => {
23
+ delete exports.state.forms[formId];
24
+ };
25
+ exports.unregisterFormSlice = unregisterFormSlice;
26
+ const unhydratedFormState = {
27
+ hydrated: false,
28
+ fieldErrors: {},
29
+ isSubmitting: false,
30
+ hasBeenSubmitted: false,
31
+ touchedFields: {},
32
+ defaultValues: {},
33
+ validateField: () => Promise.resolve(null),
34
+ registerReceiveFocus: () => () => { },
35
+ setFieldValue: () => { },
36
+ };
37
+ const useFormData = (formId) => {
38
+ var _a;
39
+ const snapshot = (0, valtio_1.useSnapshot)(exports.state);
40
+ return (_a = snapshot.forms[formId]) !== null && _a !== void 0 ? _a : unhydratedFormState;
41
+ };
42
+ exports.useFormData = useFormData;
43
+ const startSubmit = (formId) => {
44
+ exports.state.forms[formId].isSubmitting = true;
45
+ exports.state.forms[formId].hasBeenSubmitted = true;
46
+ };
47
+ exports.startSubmit = startSubmit;
48
+ const endSubmit = (formId) => {
49
+ exports.state.forms[formId].isSubmitting = false;
50
+ };
51
+ exports.endSubmit = endSubmit;
52
+ const sync = (formId, { defaultValues, action, subaction, registerReceiveFocus, validateField, setFieldValueForForm, }) => {
53
+ exports.state.forms[formId].defaultValues = defaultValues !== null && defaultValues !== void 0 ? defaultValues : {};
54
+ exports.state.forms[formId].action = action;
55
+ exports.state.forms[formId].subaction = subaction;
56
+ exports.state.forms[formId].registerReceiveFocus = registerReceiveFocus;
57
+ exports.state.forms[formId].validateField = validateField;
58
+ exports.state.forms[formId].hydrated = true;
59
+ exports.state.forms[formId].setFieldValue = setFieldValueForForm;
60
+ };
61
+ exports.sync = sync;
62
+ const clearError = (formId, fieldName) => {
63
+ delete exports.state.forms[formId].fieldErrors[fieldName];
64
+ };
65
+ exports.clearError = clearError;
66
+ const addError = (formId, fieldName, error) => {
67
+ exports.state.forms[formId].fieldErrors[fieldName] = error;
68
+ };
69
+ exports.addError = addError;
70
+ const setTouched = (formId, fieldName, touched) => {
71
+ exports.state.forms[formId].touchedFields[fieldName] = touched;
72
+ };
73
+ exports.setTouched = setTouched;
74
+ const reset = (formId) => {
75
+ exports.state.forms[formId].fieldErrors = {};
76
+ exports.state.forms[formId].touchedFields = {};
77
+ exports.state.forms[formId].hasBeenSubmitted = false;
78
+ };
79
+ exports.reset = reset;
80
+ const setFieldErrors = (formId, fieldErrors) => {
81
+ exports.state.forms[formId].fieldErrors = fieldErrors;
82
+ };
83
+ exports.setFieldErrors = setFieldErrors;
@@ -0,0 +1,47 @@
1
+ import { FieldErrors, TouchedFields } from "..";
2
+ export declare type InternalFormState = {
3
+ hydrated: boolean;
4
+ fieldErrors: FieldErrors;
5
+ isSubmitting: boolean;
6
+ hasBeenSubmitted: boolean;
7
+ touchedFields: TouchedFields;
8
+ action?: string;
9
+ subaction?: string;
10
+ defaultValues: {
11
+ [fieldName: string]: any;
12
+ };
13
+ validateField: (fieldName: string) => Promise<string | null>;
14
+ registerReceiveFocus: (fieldName: string, handler: () => void) => () => void;
15
+ setFieldValue: (fieldName: string, value: unknown) => void;
16
+ };
17
+ declare type Helpers = {
18
+ startSubmit: () => void;
19
+ endSubmit: () => void;
20
+ sync: (args: SyncFormArgs) => void;
21
+ clearError: (name: string) => void;
22
+ addError: (name: string, error: string) => void;
23
+ setTouched: (name: string, touched: boolean) => void;
24
+ reset: () => void;
25
+ setFieldErrors: (fieldErrors: FieldErrors) => void;
26
+ register: (init: SyncFormArgs) => void;
27
+ unregister: () => void;
28
+ };
29
+ declare type SyncFormArgs = {
30
+ defaultValues?: {
31
+ [fieldName: string]: any;
32
+ };
33
+ action?: string;
34
+ subaction?: string;
35
+ validateField: InternalFormState["validateField"];
36
+ registerReceiveFocus: InternalFormState["registerReceiveFocus"];
37
+ setFieldValueForForm: InternalFormState["setFieldValue"];
38
+ };
39
+ declare type StoreState = {
40
+ forms: {
41
+ [formId: string | symbol]: InternalFormState;
42
+ };
43
+ form: (formId: string | symbol) => InternalFormState;
44
+ helpers: (formId: string | symbol) => Helpers;
45
+ };
46
+ export declare const useStore: import("zustand").UseBoundStore<StoreState, import("zustand").StoreApi<StoreState>>;
47
+ export {};
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.useStore = void 0;
7
+ const zustand_1 = __importDefault(require("zustand"));
8
+ const immerMiddleware_1 = require("./immerMiddleware");
9
+ const unhydratedFormState = {
10
+ hydrated: false,
11
+ fieldErrors: {},
12
+ isSubmitting: false,
13
+ hasBeenSubmitted: false,
14
+ touchedFields: {},
15
+ defaultValues: {},
16
+ validateField: () => Promise.resolve(null),
17
+ registerReceiveFocus: () => () => { },
18
+ setFieldValue: () => { },
19
+ // clearError: () => {},
20
+ // addError: () => {},
21
+ // setTouched: () => {},
22
+ // reset: () => {},
23
+ // startSubmit: () => {},
24
+ // endSubmit: () => {},
25
+ // sync: () => {},
26
+ // setFieldErrors: () => {},
27
+ };
28
+ exports.useStore = (0, zustand_1.default)((0, immerMiddleware_1.immer)((set, get) => ({
29
+ forms: {},
30
+ form: (formId) => { var _a; return (_a = get().forms[formId]) !== null && _a !== void 0 ? _a : unhydratedFormState; },
31
+ helpers: (formId) => ({
32
+ clearError: (name) => set((state) => {
33
+ delete state.forms[formId].fieldErrors[name];
34
+ }),
35
+ addError: (name, error) => set((state) => {
36
+ state.forms[formId].fieldErrors[name] = error;
37
+ }),
38
+ setTouched: (name, touched) => set((state) => {
39
+ state.forms[formId].touchedFields[name] = touched;
40
+ }),
41
+ reset: () => set((state) => {
42
+ state.forms[formId].fieldErrors = {};
43
+ state.forms[formId].touchedFields = {};
44
+ state.forms[formId].hasBeenSubmitted = false;
45
+ }),
46
+ startSubmit: () => set((state) => {
47
+ state.forms[formId].hasBeenSubmitted = true;
48
+ state.forms[formId].isSubmitting = true;
49
+ }),
50
+ endSubmit: () => set((state) => {
51
+ state.forms[formId].isSubmitting = false;
52
+ }),
53
+ setFieldErrors: (fieldErrors) => {
54
+ set((state) => {
55
+ state.forms[formId].fieldErrors = fieldErrors;
56
+ });
57
+ },
58
+ sync: ({ defaultValues, action, subaction, validateField, registerReceiveFocus, setFieldValueForForm, }) => set((state) => {
59
+ state.forms[formId].defaultValues = defaultValues !== null && defaultValues !== void 0 ? defaultValues : {};
60
+ state.forms[formId].action = action;
61
+ state.forms[formId].subaction = subaction;
62
+ state.forms[formId].registerReceiveFocus = registerReceiveFocus;
63
+ state.forms[formId].validateField = validateField;
64
+ state.forms[formId].hydrated = true;
65
+ state.forms[formId].setFieldValue = setFieldValueForForm;
66
+ }),
67
+ unregister: () => {
68
+ set((state) => {
69
+ delete state.forms[formId];
70
+ });
71
+ },
72
+ register: ({ defaultValues, action, subaction, validateField, registerReceiveFocus, setFieldValueForForm, }) => {
73
+ set((state) => {
74
+ state.forms[formId] = {
75
+ defaultValues: defaultValues !== null && defaultValues !== void 0 ? defaultValues : {},
76
+ setFieldValue: setFieldValueForForm,
77
+ registerReceiveFocus,
78
+ validateField,
79
+ action,
80
+ subaction,
81
+ hydrated: true,
82
+ fieldErrors: {},
83
+ isSubmitting: false,
84
+ hasBeenSubmitted: false,
85
+ touchedFields: {},
86
+ // helpers
87
+ };
88
+ });
89
+ },
90
+ }),
91
+ })));
@@ -2,3 +2,4 @@ import type React from "react";
2
2
  export declare const omit: (obj: any, ...keys: string[]) => any;
3
3
  export declare const mergeRefs: <T = any>(refs: (React.MutableRefObject<T> | React.LegacyRef<T> | undefined)[]) => (instance: T | null) => void;
4
4
  export declare const useIsomorphicLayoutEffect: typeof React.useEffect;
5
+ export declare const useDeepEqualsMemo: <T>(item: T) => T;
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useIsomorphicLayoutEffect = exports.mergeRefs = exports.omit = void 0;
6
+ exports.useDeepEqualsMemo = exports.useIsomorphicLayoutEffect = exports.mergeRefs = exports.omit = void 0;
7
+ const isEqual_1 = __importDefault(require("lodash/isEqual"));
4
8
  const react_1 = require("react");
5
9
  const omit = (obj, ...keys) => {
6
10
  const result = { ...obj };
@@ -24,3 +28,14 @@ const mergeRefs = (refs) => {
24
28
  };
25
29
  exports.mergeRefs = mergeRefs;
26
30
  exports.useIsomorphicLayoutEffect = typeof window !== "undefined" ? react_1.useLayoutEffect : react_1.useEffect;
31
+ const useDeepEqualsMemo = (item) => {
32
+ const ref = (0, react_1.useRef)(item);
33
+ const areEqual = ref.current === item || (0, isEqual_1.default)(ref.current, item);
34
+ (0, react_1.useEffect)(() => {
35
+ if (!areEqual) {
36
+ ref.current = item;
37
+ }
38
+ });
39
+ return areEqual ? ref.current : item;
40
+ };
41
+ exports.useDeepEqualsMemo = useDeepEqualsMemo;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remix-validated-form",
3
- "version": "4.1.0-beta.2",
3
+ "version": "4.1.2",
4
4
  "description": "Form component and utils for easy form validation in remix",
5
5
  "browser": "./browser/index.js",
6
6
  "main": "./build/index.js",
@@ -38,6 +38,7 @@ import {
38
38
  import { useSubmitComplete } from "./internal/submissionCallbacks";
39
39
  import {
40
40
  mergeRefs,
41
+ useDeepEqualsMemo,
41
42
  useIsomorphicLayoutEffect as useLayoutEffect,
42
43
  } from "./internal/util";
43
44
  import { FieldErrors, Validator } from "./validation/types";
@@ -191,6 +192,12 @@ function formEventProxy<T extends object>(event: T): T {
191
192
  }) as T;
192
193
  }
193
194
 
195
+ const useFormAtom = (formId: string | symbol) => {
196
+ const formAtom = formRegistry(formId);
197
+ useEffect(() => () => formRegistry.remove(formId), [formId]);
198
+ return formAtom;
199
+ };
200
+
194
201
  /**
195
202
  * The primary form component of `remix-validated-form`.
196
203
  */
@@ -200,7 +207,7 @@ export function ValidatedForm<DataType>({
200
207
  children,
201
208
  fetcher,
202
209
  action,
203
- defaultValues: providedDefaultValues,
210
+ defaultValues: unMemoizedDefaults,
204
211
  formRef: formRefProp,
205
212
  onReset,
206
213
  subaction,
@@ -212,7 +219,8 @@ export function ValidatedForm<DataType>({
212
219
  ...rest
213
220
  }: FormProps<DataType>) {
214
221
  const formId = useFormId(id);
215
- const formAtom = formRegistry(formId);
222
+ const formAtom = useFormAtom(formId);
223
+ const providedDefaultValues = useDeepEqualsMemo(unMemoizedDefaults);
216
224
  const contextValue = useMemo<InternalFormContextValue>(
217
225
  () => ({
218
226
  formId,
@@ -1,4 +1,4 @@
1
- import { useRef } from "react";
1
+ import { useCallback, useRef } from "react";
2
2
 
3
3
  export class MultiValueMap<Key, Value> {
4
4
  private dict: Map<Key, Value[]> = new Map();
@@ -30,9 +30,9 @@ export class MultiValueMap<Key, Value> {
30
30
 
31
31
  export const useMultiValueMap = <Key, Value>() => {
32
32
  const ref = useRef<MultiValueMap<Key, Value> | null>(null);
33
- return () => {
33
+ return useCallback(() => {
34
34
  if (ref.current) return ref.current;
35
35
  ref.current = new MultiValueMap();
36
36
  return ref.current;
37
- };
37
+ }, []);
38
38
  };
@@ -1,5 +1,6 @@
1
+ import isEqual from "lodash/isEqual";
1
2
  import type React from "react";
2
- import { useEffect, useLayoutEffect } from "react";
3
+ import { useEffect, useLayoutEffect, useRef } from "react";
3
4
 
4
5
  export const omit = (obj: any, ...keys: string[]) => {
5
6
  const result = { ...obj };
@@ -25,3 +26,14 @@ export const mergeRefs = <T = any>(
25
26
 
26
27
  export const useIsomorphicLayoutEffect =
27
28
  typeof window !== "undefined" ? useLayoutEffect : useEffect;
29
+
30
+ export const useDeepEqualsMemo = <T>(item: T): T => {
31
+ const ref = useRef<T>(item);
32
+ const areEqual = ref.current === item || isEqual(ref.current, item);
33
+ useEffect(() => {
34
+ if (!areEqual) {
35
+ ref.current = item;
36
+ }
37
+ });
38
+ return areEqual ? ref.current : item;
39
+ };