Skip to content

Commit

Permalink
Filter out eventlistener on window from useLocalStorage hook to separ…
Browse files Browse the repository at this point in the history
…ate useReactiveLocalStorage hook
  • Loading branch information
standeren committed Oct 26, 2023
1 parent ecc403e commit 524c97f
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 17 deletions.
39 changes: 39 additions & 0 deletions frontend/packages/shared/src/hooks/useEventListener.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { renderHook } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { act } from 'react-dom/test-utils';

import { useEventListener } from './useEventListener';

const user = userEvent.setup();

const renderUseEventListener = (eventType: string, action: () => void) =>
renderHook(() => useEventListener(eventType, action), {
initialProps: { eventType, action },
});

describe('useEventListener', () => {
it('Calls action when given event happens', async () => {
const action = jest.fn();
renderUseEventListener('click', action);
await act(() => user.click(document.body));
expect(action).toHaveBeenCalledTimes(1);
});

it('Does not call action when another event is given', async () => {
const action = jest.fn();
renderUseEventListener('click', action);
await act(() => user.keyboard('{Enter}'));
expect(action).not.toHaveBeenCalled();
});

it('Removes event listener on unmount', () => {
const removeEventListenerSpy = jest.spyOn(
window,
'removeEventListener',
);
const { unmount } = renderUseEventListener('click', jest.fn());
expect(removeEventListenerSpy).not.toHaveBeenCalled();
unmount();
expect(removeEventListenerSpy).toHaveBeenCalledTimes(1);
});
});
16 changes: 16 additions & 0 deletions frontend/packages/shared/src/hooks/useEventListener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useEffect } from 'react';

/**
* Adds an event listener to the given element or the document body if no element is given. The listener is removed on unmount.
* @param eventType The event type to listen for.
* @param action The action to perform when the event is triggered.
*/
export function useEventListener(
eventType: string,
action: () => void,
) {
useEffect(() => {
window.addEventListener(eventType, action);
return () => window.removeEventListener(eventType, action);
}, [eventType, action]);
}
16 changes: 1 addition & 15 deletions frontend/packages/shared/src/hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useState } from 'react';
import { TypedStorage, typedLocalStorage } from 'app-shared/utils/webStorage';

const useWebStorage = <T>(
Expand All @@ -8,20 +8,6 @@ const useWebStorage = <T>(
): [T, (newValue: T) => void, () => void] => {
const [value, setValue] = useState<T>(typedStorage.getItem(key) || initialValue);

const handleStorageChange = useCallback(() => {
const item = typedStorage.getItem<T>(key);
setValue(item);
}, [key, setValue, typedStorage]);

useEffect(() => {

window.addEventListener('storage', handleStorageChange);

return () => {
window.removeEventListener('storage', handleStorageChange);
};
}, [handleStorageChange]);

const setStorageValue = useCallback(
(newValue: T) => {
typedStorage.setItem(key, newValue);
Expand Down
20 changes: 20 additions & 0 deletions frontend/packages/shared/src/hooks/useReactiveLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useCallback } from 'react';
import { typedLocalStorage } from 'app-shared/utils/webStorage';
import { useLocalStorage } from './useLocalStorage';
import { useEventListener } from './useEventListener';

export const useReactiveLocalStorage = <T>(
key: string,
initialValue?: T,
): [T, (newValue: T) => void, () => void] => {
const [value, setValue, removeValue] = useLocalStorage(key, initialValue);

const handleStorageChange = useCallback(() => {
const item = typedLocalStorage.getItem<T>(key);
setValue(item);

Check warning on line 14 in frontend/packages/shared/src/hooks/useReactiveLocalStorage.ts

View check run for this annotation

Codecov / codecov/patch

frontend/packages/shared/src/hooks/useReactiveLocalStorage.ts#L13-L14

Added lines #L13 - L14 were not covered by tests
}, [key, setValue]);

useEventListener('storage', handleStorageChange);

return [value, setValue, removeValue];
};
4 changes: 2 additions & 2 deletions frontend/packages/ux-editor/src/SubApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { App } from './App';
import { store } from './store';
import './styles/index.css';
import { AppContext } from './AppContext';
import { useLocalStorage } from 'app-shared/hooks/useLocalStorage';
import { useReactiveLocalStorage } from 'app-shared/hooks/useReactiveLocalStorage';
import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams';

export const SubApp = () => {
const previewIframeRef = useRef<HTMLIFrameElement>(null);
const { app } = useStudioUrlParams();
const [selectedLayoutSet, setSelectedLayoutSet, removeSelectedLayoutSet] = useLocalStorage('layoutSet/' + app, null);
const [selectedLayoutSet, setSelectedLayoutSet, removeSelectedLayoutSet] = useReactiveLocalStorage('layoutSet/' + app, null);

return (
<Provider store={store}>
Expand Down

0 comments on commit 524c97f

Please sign in to comment.