Skip to content

Commit

Permalink
Fix primefaces#2887: Storage hook respect changes in other tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware committed May 15, 2022
1 parent c28a05a commit b14fe93
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 3 deletions.
4 changes: 3 additions & 1 deletion components/lib/hooks/Hooks.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ export declare function useOverlayScrollListener(options: EventOptions): any[];
export declare function useResizeListener(options: ResizeEventOptions): any[];
export declare function useInterval(fn: any, delay?: number, when?: boolean): any[];
export declare function useTimeout(fn: any, delay?: number, when?: boolean): any[];
export declare function useStorage<S>(initialValue: S, key: string, storage?: StorageType): [S, React.Dispatch<React.SetStateAction<S>>];
export declare function useStorage<S, K extends string = string>(initialValue: S, key: K, storage?: StorageType): [S, React.Dispatch<React.SetStateAction<S>>];
export declare function useLocalStorage<S>(initialValue: S, key: string): [S, React.Dispatch<React.SetStateAction<S>>];
export declare function useSessionStorage<S>(initialValue: S, key: string): [S, React.Dispatch<React.SetStateAction<S>>];
4 changes: 2 additions & 2 deletions components/lib/hooks/Hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useOverlayListener } from './useOverlayListener';
import { useOverlayScrollListener } from './useOverlayScrollListener';
import { useResizeListener } from './useResizeListener';
import { useInterval } from './useInterval';
import { useStorage } from './useStorage';
import { useStorage, useLocalStorage, useSessionStorage } from './useStorage';
import { useTimeout } from './useTimeout';

export { usePrevious, useMountEffect, useUpdateEffect, useUnmountEffect, useEventListener, useOverlayListener, useOverlayScrollListener, useResizeListener, useInterval, useStorage, useTimeout };
export { usePrevious, useMountEffect, useUpdateEffect, useUnmountEffect, useEventListener, useOverlayListener, useOverlayScrollListener, useResizeListener, useInterval, useStorage, useLocalStorage, useSessionStorage, useTimeout };
39 changes: 39 additions & 0 deletions components/lib/hooks/useStorage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable */
import * as React from 'react';
import { useEventListener } from '../hooks/Hooks';

/**
* Hook to wrap around useState that stores the value in the browser local/session storage.
Expand All @@ -15,6 +16,17 @@ export const useStorage = (initialValue, key, storage = 'local') => {
// we check that typeof window !== 'undefined' to make SSR and SSG work properly.
const storageAvailable = typeof window !== 'undefined';

// subscribe to window storage event so changes in one tab to a stored value
// are properly reflected in all tabs
const [bindWindowStorageListener, unbindWindowStorageListener] = useEventListener({
target: 'window', type: 'storage', listener: event => {
const area = storage === 'local' ? window.localStorage : window.sessionStorage;
if (event.storageArea === area && event.key === key) {
setStoredValue(event.newValue ?? undefined);
}
}
});

const [storedValue, setStoredValue] = React.useState(() => {
if (!storageAvailable) {
return initialValue;
Expand Down Expand Up @@ -46,6 +58,33 @@ export const useStorage = (initialValue, key, storage = 'local') => {
}
};

React.useEffect(() => {
bindWindowStorageListener();
return () => unbindWindowStorageListener();
}, []);

return [storedValue, setValue];
}

/**
* Hook to wrap around useState that stores the value in the browser local storage.
*
* @param {any} initialValue the initial value to store
* @param {string} key the key to store the value in local storage
* @returns a stateful value, and a function to update it.
*/
export const useLocalStorage = (initialValue, key) => {
return useStorage(initialValue, key, 'local');
}

/**
* Hook to wrap around useState that stores the value in the browser session storage.
*
* @param {any} initialValue the initial value to store
* @param {string} key the key to store the value in session storage
* @returns a stateful value, and a function to update it.
*/
export const useSessionStorage = (initialValue, key) => {
return useStorage(initialValue, key, 'session');
}
/* eslint-enable */

0 comments on commit b14fe93

Please sign in to comment.