Navigation

React Hooks

Code snippets for Custom Hooks that mostly used in my projects

Classic useLocalStorage


import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  const isClient = typeof window === 'object';

  const [storedValue, setStoredValue] = useState(() => {
    if (!isClient) return initialValue;
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.warn(`Error reading localStorage key "${key}":`, error);
      return initialValue;
    }
  });

  useEffect(() => {
    if (!isClient) return;
    try {
      window.localStorage.setItem(key, JSON.stringify(storedValue));
    } catch (error) {
      console.warn(`Error setting localStorage key "${key}":`, error);
    }
  }, [key, storedValue, isClient]);

  return [storedValue, setStoredValue];
}

export default useLocalStorage;

useEncodeLocalStorage.ts

Please provide your own decodeBase64Unicode, encodeBase64Unicode

import { decodeBase64Unicode, encodeBase64Unicode } from "@/lib/utils";
import { useState, useEffect } from "react";

/**
 * A custom hook that syncs a state value to localStorage,
 * **Base64-encoding** the JSON string in a Unicode-safe manner.
 *
 * @param key The key to store in localStorage
 * @param initialValue The default value if none is in localStorage
 * @returns [value, setValue]
 */
export function useEncodeLocalStorage<T>(
    key: string,
    initialValue: T
): [T, React.Dispatch<React.SetStateAction<T>>] {
    const [storedValue, setStoredValue] = useState<T>(() => {
        try {
            const item = localStorage.getItem(key);
            if (item) {
                // Decode base64 safely and parse JSON
                const decoded = decodeBase64Unicode(item);
                return JSON.parse(decoded) as T;
            }
        } catch (error) {
            console.error(
                "useEncodeLocalStorage: Error loading from localStorage",
                error
            );
        }
        return initialValue;
    });

    useEffect(() => {
        try {
            const json = JSON.stringify(storedValue);
            const encoded = encodeBase64Unicode(json);
            localStorage.setItem(key, encoded);
        } catch (error) {
            console.error(
                "useEncodeLocalStorage: Error saving to localStorage",
                error
            );
        }
    }, [key, storedValue]);

    return [storedValue, setStoredValue];
}

useFetch.ts


import { FetchError } from "./../data/types";
import { useState, useCallback } from "react";

interface FetchOptions extends RequestInit {
    body?: any;
}

export interface UseFetchReturn<T> {
    data: T | null;
    setData: React.Dispatch<React.SetStateAction<T | null>>;
    loading: boolean;
    error: FetchError | null;
    setError: React.Dispatch<React.SetStateAction<FetchError | null>>;
    execute: (options?: FetchOptions) => Promise<void>;
}

const useFetch = <T = unknown>(
    initialUrl: string,
    initialOptions: FetchOptions = {}
): UseFetchReturn<T> => {
    const [url, setUrl] = useState<string>(initialUrl);
    const [options, setOptions] = useState<FetchOptions>(initialOptions);
    const [data, setData] = useState<T | null>(null);
    const [loading, setLoading] = useState<boolean>(false);
    const [error, setError] = useState<FetchError | null>(null);

    const execute = useCallback(
        async (overrideOptions?: FetchOptions) => {
            setLoading(true);
            setError(null);
            try {
                const response = await fetch(url, {
                    ...options,
                    ...overrideOptions
                });

                if (!response.ok) {
                    const errorText = await response.text();
                    throw new Error(
                        JSON.stringify(
                            {
                                status: response.status,
                                name: response.statusText,
                                message: errorText
                            },
                            null,
                            2
                        )
                    );
                }

                const responseData: T = await response.json();
                setData(responseData);
            } catch (err) {
                const errJson = JSON.parse(err?.message || "{}");
                console.log(errJson);
                setError({
                    status: errJson?.status || 500,
                    name: errJson?.name || "Error",
                    message: `${errJson?.message || "Unknown error"}`
                });
            } finally {
                setLoading(false);
            }
        },
        [url, options]
    );

    return { data, setData, loading, error, setError, execute };
};

export default useFetch;