From 609a0c217ef188b5d09956fce71b492f7d9c54d7 Mon Sep 17 00:00:00 2001 From: innovatixhub Date: Tue, 26 Nov 2024 06:15:29 -0800 Subject: [PATCH] Add files via upload --- src/hooks/App/useCurrentSkipLinkId.jsx | 19 +++++ src/hooks/App/useNavToolsProps.jsx | 51 +++++++++++++ src/hooks/App/useScrollOnMount.jsx | 11 +++ src/hooks/App/useSignOut.jsx | 38 ++++++++++ src/hooks/App/useSlider.jsx | 42 ++++++++++ .../App/useStoreWebsiteDataToLocalStorage.jsx | 16 ++++ src/hooks/App/useTimerDown.jsx | 76 +++++++++++++++++++ src/hooks/App/useUpdateLoadingOnSamePage.jsx | 38 ++++++++++ src/hooks/App/useUpdateLoadingState.jsx | 38 ++++++++++ src/hooks/Helper/useAsync.jsx | 32 ++++++++ src/hooks/Helper/useChangeLangDirOnKeys.jsx | 19 +++++ src/hooks/Helper/useCloseElement.jsx | 44 +++++++++++ src/hooks/Helper/useCopyText.jsx | 14 ++++ src/hooks/Helper/useDebounce.jsx | 8 ++ src/hooks/Helper/useEventListener.jsx | 18 +++++ src/hooks/Helper/useFormData.jsx | 36 +++++++++ src/hooks/Helper/useFunctionOnKey.jsx | 29 +++++++ src/hooks/Helper/useGetParams.jsx | 31 ++++++++ src/hooks/Helper/useGetResizeWindow.jsx | 29 +++++++ src/hooks/Helper/useGetSearchParam.jsx | 8 ++ src/hooks/Helper/useKeyPress.jsx | 23 ++++++ src/hooks/Helper/useLocalStorage.jsx | 12 +++ src/hooks/Helper/useOnScreen.jsx | 25 ++++++ src/hooks/Helper/useOnlineStatus.jsx | 30 ++++++++ src/hooks/Helper/usePageBottom.jsx | 27 +++++++ src/hooks/Helper/usePreviousState.jsx | 13 ++++ src/hooks/Helper/useRandomNumber.jsx | 26 +++++++ src/hooks/Helper/useTimeout.jsx | 32 ++++++++ src/hooks/Helper/useToggle.jsx | 14 ++++ src/hooks/Helper/useUpdateEffect.jsx | 20 +++++ 30 files changed, 819 insertions(+) create mode 100644 src/hooks/App/useCurrentSkipLinkId.jsx create mode 100644 src/hooks/App/useNavToolsProps.jsx create mode 100644 src/hooks/App/useScrollOnMount.jsx create mode 100644 src/hooks/App/useSignOut.jsx create mode 100644 src/hooks/App/useSlider.jsx create mode 100644 src/hooks/App/useStoreWebsiteDataToLocalStorage.jsx create mode 100644 src/hooks/App/useTimerDown.jsx create mode 100644 src/hooks/App/useUpdateLoadingOnSamePage.jsx create mode 100644 src/hooks/App/useUpdateLoadingState.jsx create mode 100644 src/hooks/Helper/useAsync.jsx create mode 100644 src/hooks/Helper/useChangeLangDirOnKeys.jsx create mode 100644 src/hooks/Helper/useCloseElement.jsx create mode 100644 src/hooks/Helper/useCopyText.jsx create mode 100644 src/hooks/Helper/useDebounce.jsx create mode 100644 src/hooks/Helper/useEventListener.jsx create mode 100644 src/hooks/Helper/useFormData.jsx create mode 100644 src/hooks/Helper/useFunctionOnKey.jsx create mode 100644 src/hooks/Helper/useGetParams.jsx create mode 100644 src/hooks/Helper/useGetResizeWindow.jsx create mode 100644 src/hooks/Helper/useGetSearchParam.jsx create mode 100644 src/hooks/Helper/useKeyPress.jsx create mode 100644 src/hooks/Helper/useLocalStorage.jsx create mode 100644 src/hooks/Helper/useOnScreen.jsx create mode 100644 src/hooks/Helper/useOnlineStatus.jsx create mode 100644 src/hooks/Helper/usePageBottom.jsx create mode 100644 src/hooks/Helper/usePreviousState.jsx create mode 100644 src/hooks/Helper/useRandomNumber.jsx create mode 100644 src/hooks/Helper/useTimeout.jsx create mode 100644 src/hooks/Helper/useToggle.jsx create mode 100644 src/hooks/Helper/useUpdateEffect.jsx diff --git a/src/hooks/App/useCurrentSkipLinkId.jsx b/src/hooks/App/useCurrentSkipLinkId.jsx new file mode 100644 index 0000000..11a429d --- /dev/null +++ b/src/hooks/App/useCurrentSkipLinkId.jsx @@ -0,0 +1,19 @@ +import { useLocation } from "react-router-dom"; +import { PAGE_SECTIONS_ID } from "src/Data/pagesData"; + +const useCurrentSkipLinkId = () => { + const { pathname } = useLocation(); + + function findSectionLinkIdByPath() { + return PAGE_SECTIONS_ID.find( + (sectionData) => sectionData.pagePath === pathname + )?.sectionId; + } + + const sectionId = findSectionLinkIdByPath(); + const defaultSectionId = + PAGE_SECTIONS_ID[PAGE_SECTIONS_ID.length - 1].sectionId; + + return sectionId || defaultSectionId; +}; +export default useCurrentSkipLinkId; diff --git a/src/hooks/App/useNavToolsProps.jsx b/src/hooks/App/useNavToolsProps.jsx new file mode 100644 index 0000000..f14590a --- /dev/null +++ b/src/hooks/App/useNavToolsProps.jsx @@ -0,0 +1,51 @@ +import { useEffect, useState } from "react"; +import { useSelector } from "react-redux"; +import { useLocation } from "react-router-dom"; + +const useNavToolsProps = () => { + const [navToolsProps, setNavToolsProps] = useState({}); + const { + loginInfo: { isSignIn }, + } = useSelector((state) => state.user); + const location = useLocation(); + const path = location.pathname; + const navProps = { + signIn: { + showHeart: true, + showCart: true, + showUser: true, + }, + notSignIn: { + showHeart: false, + showCart: false, + showUser: false, + }, + signUpPage: { + showHeart: false, + showCart: false, + showUser: false, + }, + }; + + const setSelectedNavProps = () => { + let selectedNavProps = navProps.default; + + if (!isSignIn) { + selectedNavProps = navProps.notSignIn; + } else if (path === "/signup" || path === "/login") { + selectedNavProps = navProps.signUpPage; + } else if (isSignIn) { + selectedNavProps = navProps.signIn; + } + + setNavToolsProps(selectedNavProps); + }; + + useEffect(() => { + setSelectedNavProps(); + }, [isSignIn, path]); + + return navToolsProps; +}; + +export default useNavToolsProps; diff --git a/src/hooks/App/useScrollOnMount.jsx b/src/hooks/App/useScrollOnMount.jsx new file mode 100644 index 0000000..95d96fc --- /dev/null +++ b/src/hooks/App/useScrollOnMount.jsx @@ -0,0 +1,11 @@ +import { useEffect } from "react"; + +const useScrollOnMount = (scrollY = 0) => { + const scrollBehavior = "instant"; + + useEffect(() => { + window.scrollTo({ top: 0, behavior: scrollBehavior }); + window.scrollTo({ top: scrollY, behavior: scrollBehavior }); + }, []); +}; +export default useScrollOnMount; diff --git a/src/hooks/App/useSignOut.jsx b/src/hooks/App/useSignOut.jsx new file mode 100644 index 0000000..fb6d9a5 --- /dev/null +++ b/src/hooks/App/useSignOut.jsx @@ -0,0 +1,38 @@ +import { useTranslation } from "react-i18next"; +import { useDispatch } from "react-redux"; +import { showAlert } from "src/Features/alertsSlice"; +import { setEmptyArrays } from "src/Features/productsSlice"; +import { signOut } from "src/Features/userSlice"; + +const useSignOut = () => { + const { t } = useTranslation(); + const dispatch = useDispatch(); + const arraysToEmpty = [ + "favoritesProducts", + "searchProducts", + "orderProducts", + "cartProducts", + "wishList", + ]; + + const handleSignOut = () => { + const emptyArraysAction = setEmptyArrays({ keys: arraysToEmpty }); + + dispatch(emptyArraysAction); + dispatch(signOut()); + + setTimeout(() => { + dispatch( + showAlert({ + alertText: t("toastAlert.signOutSuccess"), + alertState: "warning", + alertType: "alert", + }) + ); + }, 500); + }; + + return handleSignOut; +}; + +export default useSignOut; diff --git a/src/hooks/App/useSlider.jsx b/src/hooks/App/useSlider.jsx new file mode 100644 index 0000000..e1f7949 --- /dev/null +++ b/src/hooks/App/useSlider.jsx @@ -0,0 +1,42 @@ +import { useRef } from "react"; +import { getScrollSliderValue } from "src/Functions/componentsFunctions"; +import { buttonEffect } from "src/Functions/effects"; + +const useSlider = (sliderRef) => { + const isSliderClicked = useRef(false); + + function handlePrevBtn(e) { + const isFirstSlide = sliderRef.current.scrollLeft === 0; + if (isFirstSlide) return; + + buttonEffect(e); + + if (!isSliderClicked.current) isSliderClicked.current = true; + else return; + setTimeout(() => (isSliderClicked.current = false), 500); + + sliderRef.current.scrollLeft -= getScrollSliderValue(sliderRef.current); + } + + function handleNextBtn(e) { + if (isLastSlide(sliderRef)) return; + + buttonEffect(e); + + if (!isSliderClicked.current) isSliderClicked.current = true; + else return; + setTimeout(() => (isSliderClicked.current = false), 500); + + sliderRef.current.scrollLeft += getScrollSliderValue(sliderRef.current); + } + + return { isSliderClicked, handleNextBtn, handlePrevBtn }; +}; +export default useSlider; + +export function isLastSlide(sliderRef) { + const sliderEle = sliderRef.current; + return ( + sliderEle.scrollWidth - sliderEle.clientWidth - sliderEle.scrollLeft < 2 + ); +} diff --git a/src/hooks/App/useStoreWebsiteDataToLocalStorage.jsx b/src/hooks/App/useStoreWebsiteDataToLocalStorage.jsx new file mode 100644 index 0000000..e68943c --- /dev/null +++ b/src/hooks/App/useStoreWebsiteDataToLocalStorage.jsx @@ -0,0 +1,16 @@ +import { useEffect } from "react"; +import { useSelector } from "react-redux"; +import { setItemToLocalStorage } from "../Helper/useLocalStorage"; + +const useStoreWebsiteDataToLocalStorage = () => { + const userData = useSelector((state) => state.user); + const productsData = useSelector((state) => state.products); + const localStorageData = useSelector((state) => state.localStorage); + + useEffect(() => { + setItemToLocalStorage("productsSliceData", productsData); + setItemToLocalStorage("userSliceData", userData); + setItemToLocalStorage("storageSliceData", localStorageData); + }, [userData, productsData, localStorageData]); +}; +export default useStoreWebsiteDataToLocalStorage; diff --git a/src/hooks/App/useTimerDown.jsx b/src/hooks/App/useTimerDown.jsx new file mode 100644 index 0000000..4658c73 --- /dev/null +++ b/src/hooks/App/useTimerDown.jsx @@ -0,0 +1,76 @@ +import { useEffect, useRef, useState } from "react"; +import { + getFormattedTime, + getTimeInMilliseconds, + getTimeObj, +} from "src/Functions/helper"; +import useLocalStorage from "../Helper/useLocalStorage"; + +/* Props Example + timeEvent="3 24 60 60" Days-Hours-Minutes-Seconds + eventName="timerName" localStorage key name +*/ + +const useTimerDown = ( + downTime, + { timeResetRequired, stopTimer, timerName, formattedTime } +) => { + if (!timerName) throw new Error("Timer name is invalid"); + if (timeResetRequired) localStorage.removeItem(timerName); + + const times = downTime.split(" "); + const timeLocal = useLocalStorage(timerName); + const timeOrTimeLocal = timeLocal + ? timeLocal + : getTimeInMilliseconds(...times); + const [time, setTime] = useState(timeOrTimeLocal); + const [timeData, setTimeData] = useState(getTimeObj(timeOrTimeLocal)); + const [isTimerDone, setIsTimerDone] = useState(false); + const isMounted = useRef(false); + let timerId; + + function useEffectTimeUpdater() { + if (time <= -1000) { + setIsTimerDone(true); + return; + } + + timerId = setTimeout(() => { + setTime(time - 1000); + + if (formattedTime) { + setTimeData(getFormattedTime(getTimeObj(time))); + useLocalStorage(timerName, time); + return; + } + + setTimeData(getTimeObj(time)); + useLocalStorage(timerName, time); + }, 1000); + + return () => { + clearTimeout(timerId); + }; + } + + useEffect(() => { + if (!isMounted.current) { + isMounted.current = true; + + if (formattedTime) { + setTimeData(getFormattedTime(getTimeObj(time))); + useLocalStorage(timerName, time); + useEffectTimeUpdater(); + return; + } + } + + if (stopTimer) return; + + useEffectTimeUpdater(); + }, [time]); + + return { timeData, isTimerDone }; +}; + +export default useTimerDown; diff --git a/src/hooks/App/useUpdateLoadingOnSamePage.jsx b/src/hooks/App/useUpdateLoadingOnSamePage.jsx new file mode 100644 index 0000000..c530e7e --- /dev/null +++ b/src/hooks/App/useUpdateLoadingOnSamePage.jsx @@ -0,0 +1,38 @@ +import { useEffect, useRef } from "react"; +import { useDispatch } from "react-redux"; +import { getRandomItem } from "src/Functions/helper"; + +const useUpdateLoadingOnSamePage = ({ + loadingState, + loadingKey, + cleanFunction, + delays, + dependencies = [], + actionMethod, +}) => { + const dispatch = useDispatch(); + const timerId = useRef(null); + let randomDelay = getRandomItem(delays); + + function updateLoadingState() { + dispatch(actionMethod({ key: loadingKey, value: true })); + + timerId.current = setTimeout(() => { + dispatch(actionMethod({ key: loadingKey, value: false })); + }, randomDelay); + + randomDelay = getRandomItem(delays); + } + + function useEffectFunction() { + updateLoadingState(); + + return () => { + clearTimeout(timerId.current); + if (cleanFunction) cleanFunction(); + }; + } + + useEffect(useEffectFunction, dependencies); +}; +export default useUpdateLoadingOnSamePage; diff --git a/src/hooks/App/useUpdateLoadingState.jsx b/src/hooks/App/useUpdateLoadingState.jsx new file mode 100644 index 0000000..6fc78e3 --- /dev/null +++ b/src/hooks/App/useUpdateLoadingState.jsx @@ -0,0 +1,38 @@ +import { useEffect, useRef } from "react"; +import { useDispatch } from "react-redux"; +import { getRandomItem } from "src/Functions/helper"; + +const useUpdateLoadingState = ({ + loadingState, + loadingKey, + cleanFunction, + delays, + dependencies = [], + actionMethod, +}) => { + const dispatch = useDispatch(); + const timerId = useRef(null); + let randomDelay = getRandomItem(delays); + + function updateLoadingState() { + if (!loadingState) return; + + timerId.current = setTimeout(() => { + dispatch(actionMethod({ key: loadingKey, value: false })); + }, randomDelay); + + randomDelay = getRandomItem(delays); + } + + function useEffectFunction() { + updateLoadingState(); + + return () => { + clearTimeout(timerId.current); + if (cleanFunction) cleanFunction(); + }; + } + + useEffect(useEffectFunction, dependencies); +}; +export default useUpdateLoadingState; diff --git a/src/hooks/Helper/useAsync.jsx b/src/hooks/Helper/useAsync.jsx new file mode 100644 index 0000000..e8457c3 --- /dev/null +++ b/src/hooks/Helper/useAsync.jsx @@ -0,0 +1,32 @@ +import axios from "axios"; +import { useEffect, useState } from "react"; + +const useAsync = (api, options = {}, dependencies = []) => { + const [isLoading, setIsLoading] = useState(false); + const [isError, setIsError] = useState(false); + const [isDone, setIsDone] = useState(false); + const [data, setData] = useState(null); + + async function fetchData() { + setIsDone(false); + try { + setIsLoading(true); + const res = await axios(api, options); + setData(res.data); + } catch (err) { + setIsError(true); + throw new Error(err); + } finally { + setIsLoading(false); + setIsDone(true); + } + } + + useEffect(() => { + fetchData(); + }, dependencies); + + return [data, isError, isLoading, isDone]; +}; + +export default useAsync; diff --git a/src/hooks/Helper/useChangeLangDirOnKeys.jsx b/src/hooks/Helper/useChangeLangDirOnKeys.jsx new file mode 100644 index 0000000..eb8dcea --- /dev/null +++ b/src/hooks/Helper/useChangeLangDirOnKeys.jsx @@ -0,0 +1,19 @@ +import i18n from "i18next"; +import useFunctionOnKey from "./useFunctionOnKey"; + +const DELAY = 0; + +const useChangeLangDirOnKeys = () => { + function changeLang(lang) { + i18n.changeLanguage(lang); + } + + useFunctionOnKey(() => changeLang("en"), ["KeyE"], DELAY); + useFunctionOnKey(() => changeLang("ar"), ["KeyA"], DELAY); + useFunctionOnKey(() => changeLang("ru"), ["KeyR"], DELAY); + useFunctionOnKey(() => changeLang("fr"), ["KeyF"], DELAY); + useFunctionOnKey(() => changeLang("ja"), ["KeyJ"], DELAY); + useFunctionOnKey(() => changeLang("hu"), ["KeyH"], DELAY); + useFunctionOnKey(() => changeLang("hi"), ["KeyI"], DELAY); +}; +export default useChangeLangDirOnKeys; diff --git a/src/hooks/Helper/useCloseElement.jsx b/src/hooks/Helper/useCloseElement.jsx new file mode 100644 index 0000000..d0780e8 --- /dev/null +++ b/src/hooks/Helper/useCloseElement.jsx @@ -0,0 +1,44 @@ +import { useEffect, useState } from "react"; + +const useCloseElement = (toggleEleRef, switcherEleRef, exceptElementRef) => { + const [isElementClose, setIsElementClose] = useState(false); + + function handleDocumentClick(e) { + if (!toggleEleRef.current || !switcherEleRef.current) return; + + const target = e.target; + const isSwitcherEle = target === switcherEleRef?.current; + const isExceptEle = target === exceptElementRef?.current; + const isInsideToggle = isParentOfElement(target, toggleEleRef?.current); + const shouldCloseElement = + (!isSwitcherEle && !isInsideToggle) || isExceptEle; + + if (shouldCloseElement) setIsElementClose(false); + else if (isSwitcherEle) setIsElementClose((prevState) => !prevState); + } + + useEffect(() => { + window.addEventListener("click", handleDocumentClick); + + return () => window.removeEventListener("click", handleDocumentClick); + }, [toggleEleRef, switcherEleRef, exceptElementRef]); + + return [isElementClose, setIsElementClose]; +}; + +export default useCloseElement; + +/* Helper Function */ +const isParentOfElement = (element, requiredEle) => { + let parentElement = element.parentElement; + + while ( + parentElement && + requiredEle !== parentElement && + requiredEle !== element + ) { + parentElement = parentElement.parentElement; + } + + return !!parentElement; +}; diff --git a/src/hooks/Helper/useCopyText.jsx b/src/hooks/Helper/useCopyText.jsx new file mode 100644 index 0000000..a5ec584 --- /dev/null +++ b/src/hooks/Helper/useCopyText.jsx @@ -0,0 +1,14 @@ +import { useState } from "react"; + +const useCopyText = () => { + const [copiedText, setCopiedText] = useState(""); + + function setCopy(text) { + navigator.clipboard.writeText(text); + setCopiedText(text); + } + + return [copiedText, setCopy]; +}; + +export default useCopyText; diff --git a/src/hooks/Helper/useDebounce.jsx b/src/hooks/Helper/useDebounce.jsx new file mode 100644 index 0000000..fef60e2 --- /dev/null +++ b/src/hooks/Helper/useDebounce.jsx @@ -0,0 +1,8 @@ +import { useEffect } from "react"; +import useTimeout from "./useTimeout"; + +export default function useDebounce(callback, delay = 500, dependencies = []) { + const { reset, clear } = useTimeout(callback, delay); + useEffect(reset, [...dependencies, reset]); + useEffect(clear, []); +} diff --git a/src/hooks/Helper/useEventListener.jsx b/src/hooks/Helper/useEventListener.jsx new file mode 100644 index 0000000..fa1ba0e --- /dev/null +++ b/src/hooks/Helper/useEventListener.jsx @@ -0,0 +1,18 @@ +import { useEffect } from "react"; + +const useEventListener = ( + ref, + eventName, + callback, + dependencies = [ref, eventName, callback] +) => { + useEffect(() => { + const element = ref.current ? ref.current : ref; + + element?.addEventListener(eventName, callback); + + return () => element?.removeEventListener(eventName, callback); + }, dependencies); +}; + +export default useEventListener; diff --git a/src/hooks/Helper/useFormData.jsx b/src/hooks/Helper/useFormData.jsx new file mode 100644 index 0000000..f31d515 --- /dev/null +++ b/src/hooks/Helper/useFormData.jsx @@ -0,0 +1,36 @@ +import { useState } from "react"; +import { setItemToLocalStorage } from "./useLocalStorage"; + +const useFormData = ({ + initialValues, + onSubmit, + storeInLocalStorage, + localStorageKey, +}) => { + const valuesLocal = localStorage.getItem(localStorageKey); + const hasDataInLocal = valuesLocal && storeInLocalStorage; + + const [values, setValues] = useState( + hasDataInLocal ? JSON.parse(valuesLocal) : initialValues + ); + + function handleChange(event) { + const { name, value } = event.target; + + setValues((prevValues) => { + const values = { ...prevValues, [name]: value }; + + if (storeInLocalStorage) setItemToLocalStorage(localStorageKey, values); + return values; + }); + } + + const handleSubmit = (event) => { + event.preventDefault(); + onSubmit(values); + }; + + return { values, handleChange, handleSubmit }; +}; + +export default useFormData; diff --git a/src/hooks/Helper/useFunctionOnKey.jsx b/src/hooks/Helper/useFunctionOnKey.jsx new file mode 100644 index 0000000..ebe1aa5 --- /dev/null +++ b/src/hooks/Helper/useFunctionOnKey.jsx @@ -0,0 +1,29 @@ +import useDebounce from "./useDebounce"; +import useKeyPress from "./useKeyPress"; + +const useFunctionOnKey = ( + callback, + keysNames, + delay = 200, + disableMainKeys = false, + disableOnFocus = false +) => { + const [pressedKey, pressInfo] = useKeyPress(); + useDebounce(() => executeOnClick(), delay, [pressedKey, pressInfo]); + + function executeOnClick() { + const { shiftKey, altKey, ctrlKey } = pressInfo; + const isOneOfMainKeysPressed = shiftKey || altKey || ctrlKey; + const focusElement = document.activeElement?.tagName; + const isFocusOnInput = /^(input|textarea)$/i.test(focusElement); + const shouldRejectExecution = + (disableMainKeys || disableOnFocus) && + (isOneOfMainKeysPressed || isFocusOnInput); + + if (shouldRejectExecution) return; + + if (keysNames.includes(pressedKey)) callback(); + } +}; + +export default useFunctionOnKey; diff --git a/src/hooks/Helper/useGetParams.jsx b/src/hooks/Helper/useGetParams.jsx new file mode 100644 index 0000000..ffa62cf --- /dev/null +++ b/src/hooks/Helper/useGetParams.jsx @@ -0,0 +1,31 @@ +import { useEffect, useState } from "react"; + +const useGetParams = () => { + const [params, setParams] = useState({}); + + useEffect(() => { + const updateParams = () => { + const url = window.location.href; + const paramsStr = url.split("?")[1]; + const paramsArray = paramsStr ? paramsStr.split("&") : []; + let allParams = {}; + + paramsArray.forEach((param) => { + const [paramKey, paramValue] = param.split("="); + allParams = { ...allParams, [paramKey]: paramValue }; + }); + + setParams(allParams); + }; + + updateParams(); + + window.addEventListener("popstate", updateParams); + + return () => window.removeEventListener("popstate", updateParams); + }, []); + + return params; +}; + +export default useGetParams; diff --git a/src/hooks/Helper/useGetResizeWindow.jsx b/src/hooks/Helper/useGetResizeWindow.jsx new file mode 100644 index 0000000..03bde9b --- /dev/null +++ b/src/hooks/Helper/useGetResizeWindow.jsx @@ -0,0 +1,29 @@ +import { useEffect, useState } from "react"; + +const useGetResizeWindow = () => { + const [sizes, setSizes] = useState({ + windowWidth: innerWidth, + windowHeight: innerHeight, + }); + let timerId; + + useEffect(() => { + function handleResize() { + clearTimeout(timerId); + + timerId = setTimeout(() => { + setSizes({ + windowWidth: innerWidth, + windowHeight: innerHeight, + }); + }, 300); + } + + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, []); + + return sizes; +}; + +export default useGetResizeWindow; diff --git a/src/hooks/Helper/useGetSearchParam.jsx b/src/hooks/Helper/useGetSearchParam.jsx new file mode 100644 index 0000000..1efe8f8 --- /dev/null +++ b/src/hooks/Helper/useGetSearchParam.jsx @@ -0,0 +1,8 @@ +import { useSearchParams } from "react-router-dom"; + +const useGetSearchParam = (key) => { + const [searchParams] = useSearchParams(); + const searchParam = searchParams.get(key); + return searchParam; +}; +export default useGetSearchParam; diff --git a/src/hooks/Helper/useKeyPress.jsx b/src/hooks/Helper/useKeyPress.jsx new file mode 100644 index 0000000..d4b66da --- /dev/null +++ b/src/hooks/Helper/useKeyPress.jsx @@ -0,0 +1,23 @@ +import { useEffect, useState } from "react"; + +const useKeyPress = () => { + const [pressInfo, setPressInfo] = useState({}); + const [key, setKey] = useState(""); + + function handleKeyPress(e) { + const { altKey, ctrlKey, shiftKey, target, timeStamp, keyCode } = e; + const extractedInfo = { altKey, ctrlKey, shiftKey, target, timeStamp, keyCode }; + setPressInfo(extractedInfo); + setKey(e.code); + } + + useEffect(() => { + window.addEventListener("keydown", handleKeyPress); + + return () => window.removeEventListener("keydown", handleKeyPress); + }, []); + + return [key, pressInfo]; +}; + +export default useKeyPress; diff --git a/src/hooks/Helper/useLocalStorage.jsx b/src/hooks/Helper/useLocalStorage.jsx new file mode 100644 index 0000000..b86eab4 --- /dev/null +++ b/src/hooks/Helper/useLocalStorage.jsx @@ -0,0 +1,12 @@ +const useLocalStorage = (keyName, data) => { + const localData = localStorage.getItem(keyName); + if (!data) return JSON.parse(localData); + + setItemToLocalStorage(keyName, data); + return JSON.parse(localData); +}; +export default useLocalStorage; + +export function setItemToLocalStorage(key, data) { + localStorage.setItem(key, JSON.stringify(data)); +} diff --git a/src/hooks/Helper/useOnScreen.jsx b/src/hooks/Helper/useOnScreen.jsx new file mode 100644 index 0000000..91418d3 --- /dev/null +++ b/src/hooks/Helper/useOnScreen.jsx @@ -0,0 +1,25 @@ +import { useEffect, useState } from "react"; + +function useOnScreen(ref, options = { rootMargin: "0px", threshold: 1 }) { + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + if (!ref.current) return; + + const observer = new IntersectionObserver( + ([entry]) => setIsVisible(entry.isIntersecting), + options + ); + + observer.observe(ref.current); + + return () => { + if (!ref.current) return; + observer.unobserve(ref.current); + }; + }, []); + + return isVisible; +} + +export default useOnScreen; diff --git a/src/hooks/Helper/useOnlineStatus.jsx b/src/hooks/Helper/useOnlineStatus.jsx new file mode 100644 index 0000000..6a5fc60 --- /dev/null +++ b/src/hooks/Helper/useOnlineStatus.jsx @@ -0,0 +1,30 @@ +import { useEffect, useState } from "react"; + +const useOnlineStatus = () => { + const [isOnline, setIsOnline] = useState(false); + + function checkOnlineStatus() { + setIsOnline(true); + } + + function checkOfflineStatus() { + setIsOnline(false); + } + + useEffect(() => { + if (navigator.onLine) checkOnlineStatus(); + else checkOfflineStatus(); + + window.addEventListener("online", checkOnlineStatus); + window.addEventListener("offline", checkOfflineStatus); + + return () => { + document.removeEventListener("online", checkOnlineStatus); + document.removeEventListener("offline", checkOfflineStatus); + }; + }, []); + + return isOnline; +}; + +export default useOnlineStatus; diff --git a/src/hooks/Helper/usePageBottom.jsx b/src/hooks/Helper/usePageBottom.jsx new file mode 100644 index 0000000..81eed24 --- /dev/null +++ b/src/hooks/Helper/usePageBottom.jsx @@ -0,0 +1,27 @@ +import { useEffect, useState } from "react"; + +const usePageBottom = (marginBottom = 1) => { + const [isScrolledToBottom, setIsScrolledToBottom] = useState(false); + + function handleScroll() { + const scrollPosition = window.scrollY; + const windowHeight = window.innerHeight; + const htmlElement = document.documentElement; + const documentHeight = Math.max( + htmlElement.clientHeight, + htmlElement.scrollHeight, + htmlElement.offsetHeight + ); + setIsScrolledToBottom(documentHeight - windowHeight <= scrollPosition + marginBottom); + } + + useEffect(() => { + window.addEventListener("scroll", handleScroll); + + return () => window.removeEventListener("scroll", handleScroll); + }, []); + + return isScrolledToBottom; +}; + +export default usePageBottom; diff --git a/src/hooks/Helper/usePreviousState.jsx b/src/hooks/Helper/usePreviousState.jsx new file mode 100644 index 0000000..d533d7a --- /dev/null +++ b/src/hooks/Helper/usePreviousState.jsx @@ -0,0 +1,13 @@ +import { useEffect, useRef } from "react"; + +const usePreviousState = (state) => { + const oldState = useRef(state); + + useEffect(() => { + oldState.current = state; + }, [state]); + + return oldState.current; +}; + +export default usePreviousState; diff --git a/src/hooks/Helper/useRandomNumber.jsx b/src/hooks/Helper/useRandomNumber.jsx new file mode 100644 index 0000000..ec8f9cf --- /dev/null +++ b/src/hooks/Helper/useRandomNumber.jsx @@ -0,0 +1,26 @@ +import { useState } from "react"; + +const useRandomNumber = (min = 0, max = 1000) => { + const [randomNumber, setRandomNumber] = useState( + generateRandomNumber(min, max) + ); + + function changeRandomNumber(newMin = min, newMax = max) { + let newRandomNumber = generateRandomNumber(newMin, newMax); + + while (newRandomNumber === randomNumber) { + newRandomNumber = generateRandomNumber(newMin, newMax); + } + + setRandomNumber(newRandomNumber); + } + + return [randomNumber, changeRandomNumber]; +}; + +export default useRandomNumber; + +/* Helper Function */ +function generateRandomNumber(min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} diff --git a/src/hooks/Helper/useTimeout.jsx b/src/hooks/Helper/useTimeout.jsx new file mode 100644 index 0000000..e19aa8f --- /dev/null +++ b/src/hooks/Helper/useTimeout.jsx @@ -0,0 +1,32 @@ +import { useCallback, useEffect, useRef } from "react"; + +function useTimeout(callback, delay) { + const callbackRef = useRef(callback); + const timeoutRef = useRef(); + + useEffect(() => { + callbackRef.current = callback; + }, [callback]); + + const set = useCallback(() => { + timeoutRef.current = setTimeout(() => callbackRef.current(), delay); + }, [delay]); + + const clear = useCallback(() => { + timeoutRef.current && clearTimeout(timeoutRef.current); + }, []); + + useEffect(() => { + set(); + return clear; + }, [delay, set, clear]); + + const reset = useCallback(() => { + clear(); + set(); + }, [clear, set]); + + return { reset, clear }; +} + +export default useTimeout; diff --git a/src/hooks/Helper/useToggle.jsx b/src/hooks/Helper/useToggle.jsx new file mode 100644 index 0000000..c9b91b7 --- /dev/null +++ b/src/hooks/Helper/useToggle.jsx @@ -0,0 +1,14 @@ +import { useState } from "react"; + +const useToggle = (initialValue = false) => { + const [value, setValue] = useState(initialValue); + + function toggleValue(value) { + setValue((currentValue) => + typeof value === "boolean" ? value : !currentValue + ); + } + + return [value, toggleValue]; +}; +export default useToggle; diff --git a/src/hooks/Helper/useUpdateEffect.jsx b/src/hooks/Helper/useUpdateEffect.jsx new file mode 100644 index 0000000..69d09ad --- /dev/null +++ b/src/hooks/Helper/useUpdateEffect.jsx @@ -0,0 +1,20 @@ +import { useEffect, useState } from "react"; + +const useUpdateEffect = (effect, dependencies, cleanup) => { + const [isInitialRender, setIsInitialRender] = useState(true); + + useEffect(() => { + if (isInitialRender) { + setIsInitialRender(false); + return; + } + + effect(); + + return () => { + if (cleanup) cleanup(); + }; + }, dependencies); +}; + +export default useUpdateEffect;