From 1bc1e49d85e60d1ccf36c80df65f0fcbc7d1e3ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 13 Dec 2024 14:14:01 +0100 Subject: [PATCH] Add `useWeakSharedState` --- .../helpers/__tests__/useSharedState.test.ts | 75 +++++++++++++------ .../src/shared/helpers/useSharedState.tsx | 7 +- 2 files changed, 56 insertions(+), 26 deletions(-) diff --git a/packages/dnb-eufemia/src/shared/helpers/__tests__/useSharedState.test.ts b/packages/dnb-eufemia/src/shared/helpers/__tests__/useSharedState.test.ts index aef19b89884..07a1bced90e 100644 --- a/packages/dnb-eufemia/src/shared/helpers/__tests__/useSharedState.test.ts +++ b/packages/dnb-eufemia/src/shared/helpers/__tests__/useSharedState.test.ts @@ -5,6 +5,7 @@ import { createSharedState, SharedStateId, createReferenceKey, + useWeakSharedState, } from '../useSharedState' import { createContext } from 'react' @@ -110,30 +111,6 @@ describe('useSharedState', () => { expect(result.current.data).toEqual({ test: 'initial' }) }) - it('should delete the shared state when all components have been unmounted', () => { - const identifier = {} - - const { unmount: unmountA } = renderHook(() => - useSharedState(identifier, { test: 'initial' }) - ) - const { unmount: unmountB } = renderHook(() => - useSharedState(identifier) - ) - - const getStateOf = (identifier) => { - return createSharedState(identifier).get() - } - - expect(getStateOf(identifier)).toEqual({ test: 'initial' }) - expect(getStateOf(identifier)).toEqual({ test: 'initial' }) - - unmountA() - unmountB() - - expect(getStateOf(identifier)).toEqual(undefined) - expect(getStateOf(identifier)).toEqual(undefined) - }) - it('should return undefined data when no ID is given', () => { const { result } = renderHook(() => useSharedState(null, { test: 'initial' }) @@ -276,6 +253,56 @@ describe('useSharedState', () => { }) }) +describe('useWeakSharedState', () => { + it('should delete the shared state when all components have been unmounted', () => { + const identifier = {} + + const { unmount: unmountA } = renderHook(() => + useWeakSharedState(identifier, { test: 'initial' }) + ) + const { unmount: unmountB } = renderHook(() => + useWeakSharedState(identifier) + ) + + const getStateOf = (identifier) => { + return createSharedState(identifier).get() + } + + expect(getStateOf(identifier)).toEqual({ test: 'initial' }) + expect(getStateOf(identifier)).toEqual({ test: 'initial' }) + + unmountA() + unmountB() + + expect(getStateOf(identifier)).toEqual(undefined) + expect(getStateOf(identifier)).toEqual(undefined) + }) + + it('when not using weak, should not delete the shared state when all components have been unmounted', () => { + const identifier = {} + + const { unmount: unmountA } = renderHook(() => + useSharedState(identifier, { test: 'initial' }) + ) + const { unmount: unmountB } = renderHook(() => + useSharedState(identifier) + ) + + const getStateOf = (identifier) => { + return createSharedState(identifier).get() + } + + expect(getStateOf(identifier)).toEqual({ test: 'initial' }) + expect(getStateOf(identifier)).toEqual({ test: 'initial' }) + + unmountA() + unmountB() + + expect(getStateOf(identifier)).toEqual({ test: 'initial' }) + expect(getStateOf(identifier)).toEqual({ test: 'initial' }) + }) +}) + describe('createReferenceKey', () => { it('should return the same object for the same references', () => { const ref1 = {} diff --git a/packages/dnb-eufemia/src/shared/helpers/useSharedState.tsx b/packages/dnb-eufemia/src/shared/helpers/useSharedState.tsx index 5a8457f4fcf..3ae031e51de 100644 --- a/packages/dnb-eufemia/src/shared/helpers/useSharedState.tsx +++ b/packages/dnb-eufemia/src/shared/helpers/useSharedState.tsx @@ -20,7 +20,7 @@ export type SharedStateId = | Record /** - * Custom hook that provides shared state functionality. + * The shared state will be deleted when all components have been unmounted. */ export function useWeakSharedState< Data, @@ -45,7 +45,10 @@ export function useSharedState( /** Optional callback function to be called when the shared state is set from another instance/component. */ onChange = null, /** Optional configuration options. */ - { weak = false } = {} + { + /** When set to `true`, the shared state will be deleted when all components have been unmounted. */ + weak = false, + } = {} ) { const [, forceUpdate] = useReducer(() => ({}), {}) const hasMountedRef = useMounted()