NextJS way for accessing local or session storage variables
Tags: nextjs, react
Published: 2021-07-07 03:40:46

As a NextJS developer, you might be encountering issues involving the window object every single time.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1f9a796h86o5fvbzway6.png)
 
To resolve this, the solutions that you may have come up with must be these two: 

```javascript
if (typeof window !== 'undefined') {
 // localStorage code here
}
```

```javascript
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 the hook
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.

```typescript
{/*
 `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.

```typescript
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)](https://stackoverflow.com/questions/34589488/es6-immediately-invoked-arrow-function), 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.

## Lets make use of the hook now
First, lets import the hook. 
```react
import useStorage from 'hooks/useStorage';
```

Call the hook and destructure the getItem function from it.
```javascript
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 a setItem method

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.

```typescript
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.

```typescript
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;
};
```

## Adding a removeItem function
Similar approach to the one listed above, lets just add this to the `UseStorageReturnValue`
```javascript
// We'll set this to a return type value of void since 
// running the method always throws undefined
removeItem: (key: string, type?: StorageType) => void;
```

## Final Code
```typescript
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;
```

## Thoughts?

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. 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x8uu5c2xrgpqr0yja5m0.png)