Skip to content

Useful React Hooks That May Save Your Time

5 min read

Rizki Maulana Citra

Written by / Rizki Maulana Citra

Hooks was introduced in React version 16.8, React team said:

Hooks let you use state and other React features without writing a class. You can also build your own Hooks to share reusable stateful logic between components.

And then how do we build our own Hooks? let's build some of useful hooks that will do useful job for us.

useWindowPosition

A Custom hooks that will run an listen to an event when scrolling, returning the window position of Y axis and X axis

TSX
import { useEffect, useState } from 'react'

export interface Position {
  y: number
  x: number
}

const useWindowPosition = (): [Position['y'], Position['x']] => {
  const initialState: Position = {
    y: 0,
    x: 0
  }
  const [position, setPosition] = useState<Position>(initialState)

  useEffect(() => {
    if (typeof window !== 'undefined') {
      const listener = () => {
        setPosition((prevState) => {
          return {
            ...prevState,
            y: window.scrollY,
            x: window.scrollX
          }
        })
      }

      window.addEventListener('scroll', listener)

      return () => window.removeEventListener('scroll', listener)
    }
  }, [])

  return position
}

export default useWindowPosition

And you can use it like this

TSX
import useWindowPostition from '@/hooks/useWindowPosition'

const App: React.FC = () => {
  const pos = useWindowPosition()

  return <p>Current y axis is {pos.y}</p>
}

useQuery

Get a query parameters from the URL.

TSX
import { useMemo } from 'react'
import { useLocation } from 'react-router-dom'

const useQuery = () => {
  const { search } = useLocation()
  return useMemo(() => new URLSearchParams(search), [search])
}

export default useQuery

very simple hooks right? this hooks will help us get the value of the given parameter, example usage:

TS
import useQuery from '@/hooks/useQuery'
import { useEffect, useState } from 'react'

const App: React.FC () => {
	const query = useQuery()
	const [name, setName] = useState<string>('')

	useEffect(() => {
		const name = query.get('name') ?? 'unkown name'
		setName(name)
	}, [])

	return <h1>Hello {name}!</h1>
}

export default App

When you call the get() function, it will return the value of the given parameter, if the parameter is not found, it will return null instead, so be careful and always use null coalescing for code safety purpose.

useViewAnimate

If you work with animation, and are comfortable with framer motion you can use this hook to animate element when it is in viewport.

TSX
const useViewAnimate = () => {
  const { ref, inView } = useInView({
      triggerOnce: true,
      rootMargin: '100px 0px'
    }),
    controls = useAnimation()

  useEffect(() => {
    if (inView) controls.start('visible')

    if (!inView) controls.start('')
  }, [inView, controls])

  return { ref, controls }
}

export default useViewAnimate

Then how do exactly to animate the element?, well we call the hook!, example:

TSX
import useViewAnimate from '@/hooks/useViewAnimate'

import { motion } from 'framer-motion'

const App: React.FC = () => {
  const { ref, controls } = useViewAnimate()

  const variants = {
    hidden: {
      opacity: 0
    },
    visible: {
      opacity: 1
    }
  }

  return (
    <motion.div ref={ref} animate={controls} initial='hidden' variants={variants}>
      I will animate when in viewport!
    </motion.div>
  )
}

useDarkMode

It's no doubt that nowadays website required atleast two themes to let user switch between theme, because when at night, people will love to see dark surfaces, while on day light, they tend to switch and use light theme instead, let's build a custom hook that will handle this out, we want to have website that let user switch between light and dark mode.

TSX
import { useEffect, useState } from 'react'

export type Theme = 'light' | 'dark'

const initial = (): Theme => {
  return (localStorage.getItem('theme') as Theme) ?? 'light'
}

const useDarkMode = () => {
  const [theme, setTheme] = useState<Theme>(initial)

  const changeTheme = () => {
    setTheme((prevState) => {
      if (prevState === 'light') return 'dark'

      return 'light'
    })
  }

  useEffect(() => {
    localStorage.setItem('theme', theme)

    if (typeof window !== 'undefined') {
      document.documentElement.className = theme
    }
  }, [theme])

  return { theme, changeTheme }
}

export default useDarkMode

Then we can use it in our root files e.g: App.js int React, or _app.js in Next.js.

TSX
import useDarkMode from '@/hooks/useDarkMode'

const App: React.FC = () => {
  const { theme, changeTheme } = useDarkMode()

  return (
    <div>
      <p>Current Theme Is {theme}</p>
      <button onClick={changeTheme}>Change Theme</button>
    </div>
  )
}

useMediaQuery

Let say we want to display some random UI only when the screen is bigger than 600px, we can use this hook to check if the screen is bigger than 600px.

TSX
import { useEffect, useState } from 'react'

const useMediaQuery = (query: string) => {
  const isMatch = (query: string) => {
    if (typeof window !== 'undefined') {
      return window.matchMedia(query).matches
    }
    return false
  }

  const [matches, setMatches] = useState(isMatch(query))
  const handleChange = () => setMatches(isMatch(query))

  useEffect(() => {
    const mediaQuery = window.matchMedia(query)
    mediaQuery.addEventListener('change', handleChange)

    return () => mediaQuery.removeEventListener('change', handleChange)
  }, [query])

  return matches
}

export default useMediaQuery

Let see how we can use this hooks, the hooks will return a boolean value, and it's required to pass the media query to the hook, example:

TSX
import useMediaQuery from '@/hooks/useMediaQuery'

const App: React.FC = () => {
  const matches = useMediaQuery('(min-width: 600px)')

  return (
    <div className='container'>
      {matches ? (
        <p>I will show if screen size are equal or bigger than 600px otherwise hidden</p>
      ) : (
        <p>I will show if screen size are equal or smaller than 600px otherwise hidden</p>
      )}
    </div>
  )
}

Closing

That's all folks, there are actually a lot of hooks out there, and you can build your own hooks too, I hope this is helpful for you, see you next time!