@openmrs/esm-react-utils 10.0.1-pre.5240 → 10.0.1-pre.5251

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.
@@ -1,3 +1,3 @@
1
- [0] Successfully compiled: 53 files with swc (172.06ms)
1
+ [0] Successfully compiled: 53 files with swc (93.12ms)
2
2
  [0] swc --strip-leading-paths src -d dist exited with code 0
3
3
  [1] tsc --project tsconfig.build.json exited with code 0
@@ -1 +1 @@
1
- {"version":3,"file":"useVisit.d.ts","sourceRoot":"","sources":["../src/useVisit.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoC,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAKpF,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;IAE1B;;;OAGG;IACH,YAAY,EAAE,KAAK,GAAG,IAAI,CAAC;IAE3B;;;OAGG;IACH,2BAA2B,EAAE,OAAO,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,SAAmC,GAAG,eAAe,CA6EhH"}
1
+ {"version":3,"file":"useVisit.d.ts","sourceRoot":"","sources":["../src/useVisit.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoC,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAKpF,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;IAE1B;;;OAGG;IACH,YAAY,EAAE,KAAK,GAAG,IAAI,CAAC;IAE3B;;;OAGG;IACH,2BAA2B,EAAE,OAAO,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,SAAmC,GAAG,eAAe,CAyFhH"}
package/dist/useVisit.js CHANGED
@@ -26,7 +26,13 @@ dayjs.extend(isToday);
26
26
  * @param patientUuid Unique patient identifier `string`
27
27
  * @param representation The custom representation of the visit
28
28
  */ export function useVisit(patientUuid, representation = defaultVisitCustomRepresentation) {
29
- const { patientUuid: visitStorePatientUuid, manuallySetVisitUuid, setVisitContext } = useVisitContextStore();
29
+ // The callback we register has to exist before we read the store, but the mutate functions
30
+ // it calls come from SWR calls that are keyed off what the store holds. Registering a stable
31
+ // indirection through a ref breaks that cycle, so the store is read once per hook rather than
32
+ // once to get the key and again to register.
33
+ const mutateVisitRef = useRef(undefined);
34
+ const registeredMutateVisit = useCallback(()=>mutateVisitRef.current?.(), []);
35
+ const { patientUuid: visitStorePatientUuid, manuallySetVisitUuid, setVisitContext } = useVisitContextStore(registeredMutateVisit);
30
36
  // Ignore the visit store data if it is not for this patient
31
37
  const retrospectiveVisitUuid = patientUuid && visitStorePatientUuid == patientUuid ? manuallySetVisitUuid : null;
32
38
  const activeVisitUrlSuffix = `?patient=${patientUuid}&v=${representation}&includeInactive=false`;
@@ -69,7 +75,11 @@ dayjs.extend(isToday);
69
75
  activeMutate,
70
76
  retroMutate
71
77
  ]);
72
- useVisitContextStore(mutateVisit);
78
+ useEffect(()=>{
79
+ mutateVisitRef.current = mutateVisit;
80
+ }, [
81
+ mutateVisit
82
+ ]);
73
83
  const waitingForData = Boolean(!activeData || retrospectiveVisitUuid && !retroData);
74
84
  const hasRelevantError = Boolean(retrospectiveVisitUuid ? retroError : activeError);
75
85
  const isLoading = waitingForData && !hasRelevantError;
@@ -1,13 +1,25 @@
1
- import { type Visit, type VisitStoreState } from '@openmrs/esm-emr-api';
1
+ import { type Visit } from '@openmrs/esm-emr-api';
2
+ /**
3
+ * Invokes every registered mutate callback, revalidating visit data across the application.
4
+ *
5
+ * Iterates over a copy so that a callback which registers or unregisters another does not
6
+ * disturb the traversal.
7
+ */
8
+ declare function mutateVisit(): void;
2
9
  /**
3
10
  * A hook to return the visit context store and corresponding actions.
11
+ *
4
12
  * @param mutateVisitCallback An optional mutate callback to register. If provided, the
5
- * store action's `mutateVisit` function will invoke this callback (along with any other
6
- * callbacks also registered into the store)
13
+ * returned `mutateVisit` function will invoke this callback (along with any other
14
+ * callbacks registered by other components). Pass a callback with a stable identity;
15
+ * one that changes on every render re-registers on every render.
7
16
  * @returns
8
17
  */
9
- export declare function useVisitContextStore(mutateVisitCallback?: () => void): VisitStoreState & {
18
+ export declare function useVisitContextStore(mutateVisitCallback?: () => void): {
19
+ mutateVisit: typeof mutateVisit;
20
+ patientUuid: string | null;
21
+ manuallySetVisitUuid: string | null;
10
22
  setVisitContext: (newSelectedVisit: Visit | null) => void;
11
- mutateVisit: () => void;
12
23
  };
24
+ export {};
13
25
  //# sourceMappingURL=useVisitContextStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useVisitContextStore.d.ts","sourceRoot":"","sources":["../src/useVisitContextStore.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,KAAK,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAqBvF;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,mBAAmB,CAAC,EAAE,MAAM,IAAI;;;EA2BpE"}
1
+ {"version":3,"file":"useVisitContextStore.d.ts","sourceRoot":"","sources":["../src/useVisitContextStore.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,KAAK,EAAwB,MAAM,sBAAsB,CAAC;AASvF;;;;;GAKG;AACH,iBAAS,WAAW,SAInB;AAcD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,mBAAmB,CAAC,EAAE,MAAM,IAAI;;;;;EAiBpE"}
@@ -1,6 +1,20 @@
1
1
  import { useEffect, useId } from "react";
2
2
  import { getVisitStore } from "@openmrs/esm-emr-api";
3
3
  import { useStoreWithActions } from "./useStore.js";
4
+ /**
5
+ * The mutate callbacks to invoke when visit data changes, keyed by the `useId()` of the
6
+ * component that registered each one.
7
+ */ const mutateVisitCallbacks = new Map();
8
+ /**
9
+ * Invokes every registered mutate callback, revalidating visit data across the application.
10
+ *
11
+ * Iterates over a copy so that a callback which registers or unregisters another does not
12
+ * disturb the traversal.
13
+ */ function mutateVisit() {
14
+ for (const mutateCallback of Array.from(mutateVisitCallbacks.values())){
15
+ mutateCallback();
16
+ }
17
+ }
4
18
  const visitContextStoreActions = {
5
19
  setVisitContext (_, newSelectedVisit) {
6
20
  if (newSelectedVisit == null) {
@@ -12,48 +26,33 @@ const visitContextStoreActions = {
12
26
  manuallySetVisitUuid: newSelectedVisit.uuid,
13
27
  patientUuid: newSelectedVisit.patient?.uuid
14
28
  };
15
- },
16
- mutateVisit (currState) {
17
- for (const mutateCallback of Object.values(currState.mutateVisitCallbacks ?? {})){
18
- mutateCallback();
19
- }
20
- return {};
21
29
  }
22
30
  };
23
31
  /**
24
32
  * A hook to return the visit context store and corresponding actions.
33
+ *
25
34
  * @param mutateVisitCallback An optional mutate callback to register. If provided, the
26
- * store action's `mutateVisit` function will invoke this callback (along with any other
27
- * callbacks also registered into the store)
35
+ * returned `mutateVisit` function will invoke this callback (along with any other
36
+ * callbacks registered by other components). Pass a callback with a stable identity;
37
+ * one that changes on every render re-registers on every render.
28
38
  * @returns
29
39
  */ export function useVisitContextStore(mutateVisitCallback) {
30
40
  const id = useId();
31
41
  useEffect(()=>{
32
- const visitStore = getVisitStore();
33
- // register the callback if it exists
34
- if (mutateVisitCallback) {
35
- visitStore.setState({
36
- mutateVisitCallbacks: {
37
- ...visitStore.getState().mutateVisitCallbacks,
38
- [id]: mutateVisitCallback
39
- }
40
- });
42
+ if (!mutateVisitCallback) {
43
+ return;
41
44
  }
42
- // deregister the callback on unmount, if it exists
45
+ mutateVisitCallbacks.set(id, mutateVisitCallback);
43
46
  return ()=>{
44
- if (mutateVisitCallback) {
45
- const mutateVisitCallbacks = {
46
- ...visitStore.getState().mutateVisitCallbacks
47
- };
48
- delete mutateVisitCallbacks[id];
49
- visitStore.setState({
50
- mutateVisitCallbacks
51
- });
52
- }
47
+ mutateVisitCallbacks.delete(id);
53
48
  };
54
49
  }, [
55
50
  id,
56
51
  mutateVisitCallback
57
52
  ]);
58
- return useStoreWithActions(getVisitStore(), visitContextStoreActions);
53
+ const visitContextStore = useStoreWithActions(getVisitStore(), visitContextStoreActions);
54
+ return {
55
+ ...visitContextStore,
56
+ mutateVisit
57
+ };
59
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-react-utils",
3
- "version": "10.0.1-pre.5240",
3
+ "version": "10.0.1-pre.5251",
4
4
  "license": "MPL-2.0",
5
5
  "description": "React utilities for OpenMRS.",
6
6
  "type": "module",
@@ -59,17 +59,17 @@
59
59
  "single-spa-react": "^6.0.2"
60
60
  },
61
61
  "peerDependencies": {
62
- "@openmrs/esm-api": "^10.0.1-pre.5240",
63
- "@openmrs/esm-config": "^10.0.1-pre.5240",
64
- "@openmrs/esm-context": "^10.0.1-pre.5240",
65
- "@openmrs/esm-emr-api": "^10.0.1-pre.5240",
66
- "@openmrs/esm-error-handling": "^10.0.1-pre.5240",
67
- "@openmrs/esm-extensions": "^10.0.1-pre.5240",
68
- "@openmrs/esm-feature-flags": "^10.0.1-pre.5240",
69
- "@openmrs/esm-globals": "^10.0.1-pre.5240",
70
- "@openmrs/esm-navigation": "^10.0.1-pre.5240",
71
- "@openmrs/esm-state": "^10.0.1-pre.5240",
72
- "@openmrs/esm-utils": "^10.0.1-pre.5240",
62
+ "@openmrs/esm-api": "^10.0.1-pre.5251",
63
+ "@openmrs/esm-config": "^10.0.1-pre.5251",
64
+ "@openmrs/esm-context": "^10.0.1-pre.5251",
65
+ "@openmrs/esm-emr-api": "^10.0.1-pre.5251",
66
+ "@openmrs/esm-error-handling": "^10.0.1-pre.5251",
67
+ "@openmrs/esm-extensions": "^10.0.1-pre.5251",
68
+ "@openmrs/esm-feature-flags": "^10.0.1-pre.5251",
69
+ "@openmrs/esm-globals": "^10.0.1-pre.5251",
70
+ "@openmrs/esm-navigation": "^10.0.1-pre.5251",
71
+ "@openmrs/esm-state": "^10.0.1-pre.5251",
72
+ "@openmrs/esm-utils": "^10.0.1-pre.5251",
73
73
  "dayjs": "1.x",
74
74
  "i18next": "25.x",
75
75
  "react": "18.x",
@@ -79,17 +79,17 @@
79
79
  "swr": "2.x"
80
80
  },
81
81
  "devDependencies": {
82
- "@openmrs/esm-api": "10.0.1-pre.5240",
83
- "@openmrs/esm-config": "10.0.1-pre.5240",
84
- "@openmrs/esm-context": "10.0.1-pre.5240",
85
- "@openmrs/esm-emr-api": "10.0.1-pre.5240",
86
- "@openmrs/esm-error-handling": "10.0.1-pre.5240",
87
- "@openmrs/esm-extensions": "10.0.1-pre.5240",
88
- "@openmrs/esm-feature-flags": "10.0.1-pre.5240",
89
- "@openmrs/esm-globals": "10.0.1-pre.5240",
90
- "@openmrs/esm-navigation": "10.0.1-pre.5240",
91
- "@openmrs/esm-state": "10.0.1-pre.5240",
92
- "@openmrs/esm-utils": "10.0.1-pre.5240",
82
+ "@openmrs/esm-api": "10.0.1-pre.5251",
83
+ "@openmrs/esm-config": "10.0.1-pre.5251",
84
+ "@openmrs/esm-context": "10.0.1-pre.5251",
85
+ "@openmrs/esm-emr-api": "10.0.1-pre.5251",
86
+ "@openmrs/esm-error-handling": "10.0.1-pre.5251",
87
+ "@openmrs/esm-extensions": "10.0.1-pre.5251",
88
+ "@openmrs/esm-feature-flags": "10.0.1-pre.5251",
89
+ "@openmrs/esm-globals": "10.0.1-pre.5251",
90
+ "@openmrs/esm-navigation": "10.0.1-pre.5251",
91
+ "@openmrs/esm-state": "10.0.1-pre.5251",
92
+ "@openmrs/esm-utils": "10.0.1-pre.5251",
93
93
  "@swc/cli": "0.8.1",
94
94
  "@swc/core": "1.15.21",
95
95
  "@vitest/coverage-v8": "^4.1.2",
package/src/useVisit.ts CHANGED
@@ -50,7 +50,17 @@ export interface VisitReturnType {
50
50
  * @param representation The custom representation of the visit
51
51
  */
52
52
  export function useVisit(patientUuid: string, representation = defaultVisitCustomRepresentation): VisitReturnType {
53
- const { patientUuid: visitStorePatientUuid, manuallySetVisitUuid, setVisitContext } = useVisitContextStore();
53
+ // The callback we register has to exist before we read the store, but the mutate functions
54
+ // it calls come from SWR calls that are keyed off what the store holds. Registering a stable
55
+ // indirection through a ref breaks that cycle, so the store is read once per hook rather than
56
+ // once to get the key and again to register.
57
+ const mutateVisitRef = useRef<() => void>(undefined);
58
+ const registeredMutateVisit = useCallback(() => mutateVisitRef.current?.(), []);
59
+ const {
60
+ patientUuid: visitStorePatientUuid,
61
+ manuallySetVisitUuid,
62
+ setVisitContext,
63
+ } = useVisitContextStore(registeredMutateVisit);
54
64
  // Ignore the visit store data if it is not for this patient
55
65
  const retrospectiveVisitUuid = patientUuid && visitStorePatientUuid == patientUuid ? manuallySetVisitUuid : null;
56
66
  const activeVisitUrlSuffix = `?patient=${patientUuid}&v=${representation}&includeInactive=false`;
@@ -111,7 +121,9 @@ export function useVisit(patientUuid: string, representation = defaultVisitCusto
111
121
  retroMutate();
112
122
  }, [activeMutate, retroMutate]);
113
123
 
114
- useVisitContextStore(mutateVisit);
124
+ useEffect(() => {
125
+ mutateVisitRef.current = mutateVisit;
126
+ }, [mutateVisit]);
115
127
 
116
128
  const waitingForData = Boolean(!activeData || (retrospectiveVisitUuid && !retroData));
117
129
  const hasRelevantError = Boolean(retrospectiveVisitUuid ? retroError : activeError);
@@ -0,0 +1,128 @@
1
+ import React, { useCallback } from 'react';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import { act, render, renderHook } from '@testing-library/react';
4
+ import { getVisitStore } from '@openmrs/esm-emr-api';
5
+ import { useVisitContextStore } from './useVisitContextStore';
6
+
7
+ /** Counts calls to `setState` on the visit store for the duration of `run`. */
8
+ function countStoreWrites(run: () => void) {
9
+ const store = getVisitStore();
10
+ const setState = store.setState;
11
+ let writes = 0;
12
+ store.setState = ((...args: Parameters<typeof setState>) => {
13
+ writes++;
14
+ return setState(...args);
15
+ }) as typeof setState;
16
+
17
+ try {
18
+ run();
19
+ } finally {
20
+ store.setState = setState;
21
+ }
22
+
23
+ return writes;
24
+ }
25
+
26
+ describe('useVisitContextStore', () => {
27
+ it('invokes every registered callback when mutateVisit is called', () => {
28
+ const first = vi.fn();
29
+ const second = vi.fn();
30
+
31
+ const { result } = renderHook(() => useVisitContextStore(first));
32
+ renderHook(() => useVisitContextStore(second));
33
+
34
+ act(() => result.current.mutateVisit());
35
+
36
+ expect(first).toHaveBeenCalledTimes(1);
37
+ expect(second).toHaveBeenCalledTimes(1);
38
+ });
39
+
40
+ it('stops invoking a callback once its component unmounts', () => {
41
+ const callback = vi.fn();
42
+
43
+ const { result } = renderHook(() => useVisitContextStore());
44
+ const { unmount } = renderHook(() => useVisitContextStore(callback));
45
+
46
+ act(() => result.current.mutateVisit());
47
+ expect(callback).toHaveBeenCalledTimes(1);
48
+
49
+ unmount();
50
+ act(() => result.current.mutateVisit());
51
+ expect(callback).toHaveBeenCalledTimes(1);
52
+ });
53
+
54
+ it('does not write to the store when registering or unregistering a callback', () => {
55
+ let unmount = () => {};
56
+
57
+ const mountWrites = countStoreWrites(() => {
58
+ unmount = renderHook(() => useVisitContextStore(() => {})).unmount;
59
+ });
60
+ expect(mountWrites).toBe(0);
61
+
62
+ expect(countStoreWrites(unmount)).toBe(0);
63
+ });
64
+
65
+ it('does not write to the store when mutateVisit is called', () => {
66
+ const { result } = renderHook(() => useVisitContextStore(() => {}));
67
+
68
+ expect(countStoreWrites(() => act(() => result.current.mutateVisit()))).toBe(0);
69
+ });
70
+
71
+ it('keeps mutate callbacks out of the persisted store state', () => {
72
+ renderHook(() => useVisitContextStore(() => {}));
73
+
74
+ act(() => {
75
+ getVisitStore().setState({ patientUuid: 'patient-123' });
76
+ });
77
+
78
+ const persisted = JSON.parse(window.sessionStorage.getItem('openmrs:visitStoreState') ?? '{}');
79
+ expect(persisted).toEqual({ patientUuid: 'patient-123', manuallySetVisitUuid: null });
80
+ });
81
+
82
+ it('does not re-render already-mounted consumers as more consumers mount', () => {
83
+ const renderCounts: Array<number> = [];
84
+
85
+ // Memoized so that re-rendering the parent to add a row does not itself re-render the
86
+ // existing rows; anything counted here came from the store notifying its subscribers.
87
+ const Row = React.memo(function Row({ index }: { index: number }) {
88
+ renderCounts[index] = (renderCounts[index] ?? 0) + 1;
89
+ const mutate = useCallback(() => {}, []);
90
+ useVisitContextStore(mutate);
91
+ return null;
92
+ });
93
+
94
+ function Screen({ count }: { count: number }) {
95
+ return (
96
+ <>
97
+ {Array.from({ length: count }, (_, index) => (
98
+ <Row index={index} key={index} />
99
+ ))}
100
+ </>
101
+ );
102
+ }
103
+
104
+ const rows = 15;
105
+ const { rerender } = render(<Screen count={0} />);
106
+ for (let count = 1; count <= rows; count++) {
107
+ rerender(<Screen count={count} />);
108
+ }
109
+
110
+ // Each row should have rendered exactly once: mounting a row must not notify the rows
111
+ // already on screen, or filling a screen of N rows costs O(N²) renders.
112
+ expect(renderCounts).toEqual(Array.from({ length: rows }, () => 1));
113
+ });
114
+
115
+ it('re-renders consumers when the visit context itself changes', () => {
116
+ const { result } = renderHook(() => useVisitContextStore());
117
+
118
+ act(() => {
119
+ result.current.setVisitContext({
120
+ uuid: 'visit-1',
121
+ patient: { uuid: 'patient-123' },
122
+ } as Parameters<typeof result.current.setVisitContext>[0]);
123
+ });
124
+
125
+ expect(result.current.manuallySetVisitUuid).toBe('visit-1');
126
+ expect(result.current.patientUuid).toBe('patient-123');
127
+ });
128
+ });
@@ -2,6 +2,24 @@ import { useEffect, useId } from 'react';
2
2
  import { getVisitStore, type Visit, type VisitStoreState } from '@openmrs/esm-emr-api';
3
3
  import { type Actions, useStoreWithActions } from './useStore';
4
4
 
5
+ /**
6
+ * The mutate callbacks to invoke when visit data changes, keyed by the `useId()` of the
7
+ * component that registered each one.
8
+ */
9
+ const mutateVisitCallbacks = new Map<string, () => void>();
10
+
11
+ /**
12
+ * Invokes every registered mutate callback, revalidating visit data across the application.
13
+ *
14
+ * Iterates over a copy so that a callback which registers or unregisters another does not
15
+ * disturb the traversal.
16
+ */
17
+ function mutateVisit() {
18
+ for (const mutateCallback of Array.from(mutateVisitCallbacks.values())) {
19
+ mutateCallback();
20
+ }
21
+ }
22
+
5
23
  const visitContextStoreActions = {
6
24
  setVisitContext(_: VisitStoreState, newSelectedVisit: Visit | null) {
7
25
  if (newSelectedVisit == null) {
@@ -12,46 +30,32 @@ const visitContextStoreActions = {
12
30
  patientUuid: newSelectedVisit.patient?.uuid,
13
31
  };
14
32
  },
15
- mutateVisit(currState: VisitStoreState) {
16
- for (const mutateCallback of Object.values(currState.mutateVisitCallbacks ?? {})) {
17
- mutateCallback();
18
- }
19
- return {};
20
- },
21
33
  } satisfies Actions<VisitStoreState>;
22
34
 
23
35
  /**
24
36
  * A hook to return the visit context store and corresponding actions.
37
+ *
25
38
  * @param mutateVisitCallback An optional mutate callback to register. If provided, the
26
- * store action's `mutateVisit` function will invoke this callback (along with any other
27
- * callbacks also registered into the store)
39
+ * returned `mutateVisit` function will invoke this callback (along with any other
40
+ * callbacks registered by other components). Pass a callback with a stable identity;
41
+ * one that changes on every render re-registers on every render.
28
42
  * @returns
29
43
  */
30
44
  export function useVisitContextStore(mutateVisitCallback?: () => void) {
31
45
  const id = useId();
32
46
 
33
47
  useEffect(() => {
34
- const visitStore = getVisitStore();
35
-
36
- // register the callback if it exists
37
- if (mutateVisitCallback) {
38
- visitStore.setState({
39
- mutateVisitCallbacks: {
40
- ...visitStore.getState().mutateVisitCallbacks,
41
- [id]: mutateVisitCallback,
42
- },
43
- });
48
+ if (!mutateVisitCallback) {
49
+ return;
44
50
  }
45
51
 
46
- // deregister the callback on unmount, if it exists
52
+ mutateVisitCallbacks.set(id, mutateVisitCallback);
47
53
  return () => {
48
- if (mutateVisitCallback) {
49
- const mutateVisitCallbacks = { ...visitStore.getState().mutateVisitCallbacks };
50
- delete mutateVisitCallbacks[id];
51
- visitStore.setState({ mutateVisitCallbacks });
52
- }
54
+ mutateVisitCallbacks.delete(id);
53
55
  };
54
56
  }, [id, mutateVisitCallback]);
55
57
 
56
- return useStoreWithActions(getVisitStore(), visitContextStoreActions);
58
+ const visitContextStore = useStoreWithActions(getVisitStore(), visitContextStoreActions);
59
+
60
+ return { ...visitContextStore, mutateVisit };
57
61
  }