18
NextJS hook for accessing local or session storage variables
As a NextJS developer, you might be encountering issues involving the window object every single time.
To resolve this, the solutions that you may have come up with must be these two:
if (typeof window !== 'undefined') {
// localStorage code here
}
if (process.browser) {
// localStorage code here
}
However, process.browser was deprecated last January 2020. So the first one would be the ideal solution and the recommended approach. click here for more info.
Creating a react hook to handle local/session storage may come in handy since typing the first solution may be redundant if ever we are accessing local/session storage multiple times.
Lets create a file named useStorage.ts, You can name it whatever you want. But first, we need to do the typings first.
{/*
`Storage` User will determine what storage object will he/she be using.
This way, user cant pass unnecessary string values
*/}
type StorageType = 'session' | 'local';
{/*
`UseStorageReturnValue` This is just a return value type for our hook.
We can add additional typings later.
*/}
type UseStorageReturnValue = {
getItem: (key: string, type?: StorageType) => string;
};
Then, lets create the hook.
const useStorage = (): UseStorageReturnValue => {
const isBrowser: boolean = ((): boolean => typeof window !== 'undefined')();
const getItem = (key: string, type?: StorageType): string => {
const storageType: 'localStorage' | 'sessionStorage' = `${type ?? 'session'}Storage`;
return isBrowser ? window[storageType][key] : '';
};
return {
getItem,
};
};
export default useStorage;
isBrowser - This is a variable that is initialized with a function that is immediately invoked. For more info about Immediately Invoked Function Expressions (IIFE), check here. This variable checks if the user is on the client or server
getItem - Function that accepts two parameters. It just returns the value from localStorage and empty string if its undefined.
First, lets import the hook.
import useStorage from 'hooks/useStorage';
Call the hook and destructure the getItem function from it.
const { getItem } = useStorage();
const token = getItem('token');
console.log(token); // will return either a <token-value> or <''>
Thats it! Now, we can add more functionality like, setting a storage value, or deleting, and so on.
Adding additional methods may require an application to be refactored or to reuse functions as much as possible. Since we are adding the setItem method, we need to add typings accordingly.
type UseStorageReturnValue = {
getItem: (key: string, type?: StorageType) => string;
// you can set the return value to void or anything, as for my side, i just want to
// check if the value was saved or not
setItem: (key: string, value: string, type?: StorageType) => boolean;
};
Lets refactor the getItem
code and reuse the storageType
variable inside. Here, we use it as a function.
const useStorage = (): UseStorageReturnValue => {
const storageType = (type?: StorageType): 'localStorage' | 'sessionStorage' => `${type ?? 'session'}Storage`;
const isBrowser: boolean = ((): boolean => typeof window !== 'undefined')();
const getItem = (key: string, type?: StorageType): string => {
return isBrowser ? window[storageType(type)][key] : '';
};
const setItem = (key: string, value: string, type?: StorageType): boolean => {
if (isBrowser) {
window[storageType(type)].setItem(key, value);
return true;
}
return false;
};
Similar approach to the one listed above, lets just add this to the UseStorageReturnValue
// We'll set this to a return type value of void since
// running the method always throws undefined
removeItem: (key: string, type?: StorageType) => void;
type StorageType = 'session' | 'local';
type UseStorageReturnValue = {
getItem: (key: string, type?: StorageType) => string;
setItem: (key: string, value: string, type?: StorageType) => boolean;
removeItem: (key: string, type?: StorageType) => void;
};
const useStorage = (): UseStorageReturnValue => {
const storageType = (type?: StorageType): 'localStorage' | 'sessionStorage' => `${type ?? 'session'}Storage`;
const isBrowser: boolean = ((): boolean => typeof window !== 'undefined')();
const getItem = (key: string, type?: StorageType): string => {
return isBrowser ? window[storageType(type)][key] : '';
};
const setItem = (key: string, value: string, type?: StorageType): boolean => {
if (isBrowser) {
window[storageType(type)].setItem(key, value);
return true;
}
return false;
};
const removeItem = (key: string, type?: StorageType): void => {
window[storageType(type)].removeItem(key);
};
return {
getItem,
setItem,
removeItem,
};
};
export default useStorage;
This code can be refactored and methods can also be extended but I'm gonna leave it that way for now. If you have any suggestions, please feel free to comment. Thanks for reading guys.
18