From e92b7372c558fba8a5fdd673d003b4a8845d15ff Mon Sep 17 00:00:00 2001 From: melloware Date: Wed, 20 Apr 2022 07:43:08 -0400 Subject: [PATCH] Fix #2773: Add useStorage hook --- components/lib/hooks/Hooks.d.ts | 4 ++- components/lib/hooks/Hooks.js | 3 +- components/lib/hooks/useStorage.js | 51 ++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 components/lib/hooks/useStorage.js diff --git a/components/lib/hooks/Hooks.d.ts b/components/lib/hooks/Hooks.d.ts index f04546f7d5..79e573fb2a 100644 --- a/components/lib/hooks/Hooks.d.ts +++ b/components/lib/hooks/Hooks.d.ts @@ -1,6 +1,7 @@ -import { DependencyList, EffectCallback, Ref } from 'react'; +import { Dispatch, DependencyList, EffectCallback, Ref } from 'react'; export type TargetType = 'document' | 'window' | Ref | undefined; +export type StorageType = 'local' | 'session'; interface EventOptions { target?: TargetType; @@ -31,3 +32,4 @@ 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(initialValue: S, key: string, storage?: StorageType): [S, Dispatch>]; \ No newline at end of file diff --git a/components/lib/hooks/Hooks.js b/components/lib/hooks/Hooks.js index 55ee0489ce..aa9f6bddff 100644 --- a/components/lib/hooks/Hooks.js +++ b/components/lib/hooks/Hooks.js @@ -7,6 +7,7 @@ import { useOverlayListener } from './useOverlayListener'; import { useOverlayScrollListener } from './useOverlayScrollListener'; import { useResizeListener } from './useResizeListener'; import { useInterval } from './useInterval'; +import { useStorage } from './useStorage'; import { useTimeout } from './useTimeout'; -export { usePrevious, useMountEffect, useUpdateEffect, useUnmountEffect, useEventListener, useOverlayListener, useOverlayScrollListener, useResizeListener, useInterval, useTimeout }; +export { usePrevious, useMountEffect, useUpdateEffect, useUnmountEffect, useEventListener, useOverlayListener, useOverlayScrollListener, useResizeListener, useInterval, useStorage, useTimeout }; diff --git a/components/lib/hooks/useStorage.js b/components/lib/hooks/useStorage.js new file mode 100644 index 0000000000..231452e931 --- /dev/null +++ b/components/lib/hooks/useStorage.js @@ -0,0 +1,51 @@ +/* eslint-disable */ +import * as React from 'react'; + +/** + * Hook to wrap around useState that stores the value in the browser local/session storage. + * + * @param {any} initialValue the initial value to store + * @param {string} key the key to store the value in local/session storage + * @param {string} storage either 'local' or 'session' for what type of storage + * @returns a stateful value, and a function to update it. + */ +export const useStorage = (initialValue, key, storage = 'local') => { + + // Since the local storage API isn't available in server-rendering environments, + // we check that typeof window !== 'undefined' to make SSR and SSG work properly. + const storageAvailable = typeof window !== 'undefined'; + + const [storedValue, setStoredValue] = React.useState(() => { + if (!storageAvailable) { + return initialValue; + } + try { + const item = storage === 'local' ? + window.localStorage.getItem(key) : + window.sessionStorage.getItem(key); + return item ? JSON.parse(item) : initialValue; + } catch (error) { + // If error also return initialValue + return initialValue; + } + }); + + const setValue = (value) => { + try { + // Allow value to be a function so we have same API as useState + const valueToStore = value instanceof Function ? value(storedValue) : value; + setStoredValue(valueToStore); + if (storageAvailable) { + const serializedValue = JSON.stringify(valueToStore); + storage === 'local' ? + window.localStorage.setItem(key, serializedValue) : + window.sessionStorage.setItem(key, serializedValue); + } + } catch (error) { + throw new Error(`PrimeReact useStorage: Failed to serialize the value at key: ${key}`); + } + }; + + return [storedValue, setValue]; +} +/* eslint-enable */ \ No newline at end of file