Back to articles
The Magical World of React Hooks: A Dev's Easy Guide

The Magical World of React Hooks: A Dev's Easy Guide

Raymond Nwambuonwo / October 7, 2024

The Magical World of React Hooks: A Dev's Easy Guide

Table of Contents

  1. Introduction
  2. useState: The Magician's Hat
  3. useEffect: The Time Traveler
  4. useContext: The Teleporter
  5. useRef: The Sticky Note
  6. useMemo: The Smart Calculator
  7. useCallback: The Loyal Friend
  8. useReducer: The Organized Toybox
  9. useLayoutEffect: The Early Bird
  10. useImperativeHandle: The Secret Handshake
  11. useDebugValue: The Magnifying Glass
  12. Conclusion

Introduction

When I first learned about React, hooks were just emerging and understanding React was already a steep learning curve to get over. Hooks are essential to developing in React, so I decided to make this article as a way of understanding hooks in the most digestible way possible. We'll take an elemantary approach to understand hooks.

useState: The Magician's Hat

What is it?

useState is like a magician's hat. You can put things in it, take them out, and even change them!

Why is it important?

It helps your website remember things, like a user's name or their favorite color.

When to use it?

Use it when you need to keep track of information that might change.

Pros:

  • Easy to use
  • Makes your website interactive

Cons:

  • If you use it too much, your website might get slow

Metaphor:

Think of useState as a magical notepad. You can write things down, erase them, and write new things whenever you want!

Code Example:

import React, { useState } from 'react'

function MagicHat() {
  const [rabbit, setRabbit] = useState('🐰')

  return (
    <div>
      <p>What's in the hat? {rabbit}</p>
      <button onClick={()=> setRabbit('🎩')}>Change to hat</button>
    </div>
  )
}

useEffect: The Time Traveler

What is it?

useEffect is like a time machine. It lets you do things at specific times, like when your website first opens or when something changes.

Why is it important?

It helps your website react to changes and do things automatically.

When to use it?

Use it when you need to do something after your website loads or when certain things change.

Pros:

  • Automates tasks
  • Keeps your website up-to-date

Cons:

  • Can be tricky to use correctly

Metaphor:

Imagine useEffect as a helpful robot that follows your instructions to do tasks at specific times.

Code Example:

import React, { useState, useEffect } from 'react'

function TimeTravel() {
  const [time, setTime] = useState('Present')

  useEffect(() => {
    const timer = setTimeout(() => {
      setTime('Future')
    }, 3000)

    return () => clearTimeout(timer)
  }, [])

  return <p>We are in the: {time}</p>
}

useContext: The Teleporter

What is it?

useContext is like a teleporter. It can send information to any part of your website instantly!

Why is it important?

It helps you share information across your whole website without passing it through every single component.

When to use it?

Use it when you have information that lots of different parts of your website need.

Pros:

  • Makes sharing data easy
  • Keeps your code clean

Cons:

  • Can make your code harder to understand if overused

Metaphor:

Think of useContext as a magical loudspeaker that can be heard everywhere in your website-building treehouse.

Code Example:

import React, { createContext, useContext } from 'react'

const ColorContext = createContext('red')

function Teleporter() {
  const color = useContext(ColorContext)
  return <p style={{ color }}>This text is teleported!</p>
}

function App() {
  return (
    <ColorContext.Provider value='blue'>
      <Teleporter />
    </ColorContext.Provider>
  )
}

useRef: The Sticky Note

What is it?

useRef is like a sticky note. You can use it to remember things without changing your website.

Why is it important?

It helps you keep track of things or access parts of your website directly.

When to use it?

Use it when you need to remember something but don't want to update your website every time it changes.

Pros:

  • Doesn't cause re-renders
  • Useful for accessing DOM elements

Cons:

  • Can be misused to avoid proper state management

Metaphor:

Imagine useRef as a secret notebook where you can write things down without anyone else seeing.

Code Example:

import React, { useRef } from 'react'

function StickyNote() {
  const inputRef = useRef()

  const focusInput = () => {
    inputRef.current.focus()
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={focusInput}>Focus the input</button>
    </>
  )
}

useMemo: The Smart Calculator

What is it?

useMemo is like a smart calculator. It remembers the answers to hard problems so it doesn't have to solve them again.

Why is it important?

It makes your website faster by avoiding unnecessary calculations.

When to use it?

Use it when you have calculations that take a long time and don't need to be done every time your website updates.

Pros:

  • Improves performance
  • Avoids unnecessary work

Cons:

  • Can make your code more complex

Metaphor:

Think of useMemo as a wise owl that remembers the answers to difficult questions so it doesn't have to think too hard every time.

Code Example:

import React, { useMemo, useState } from 'react'

function SmartCalculator() {
  const [number, setNumber] = useState(1)

  const expensiveResult = useMemo(() => {
    console.log('Calculating...')
    return number * number
  }, [number])

  return (
    <>
      <input
        type='number'
        value={number}
        onChange={e=> setNumber(Number(e.target.value))}
      />
      <p>The square is: {expensiveResult}</p>
    </>
  )
}

useCallback: The Loyal Friend

What is it?

useCallback is like a loyal friend. It helps functions remember who they are, even when things around them change.

Why is it important?

It stops functions from being recreated unnecessarily, which can make your website faster.

When to use it?

Use it when you're passing functions to optimized child components that rely on reference equality to prevent unnecessary renders.

Pros:

  • Improves performance
  • Helps with optimization

Cons:

  • Can be overused, leading to premature optimization

Metaphor:

Imagine useCallback as a name tag for your functions. It helps them keep their identity even when everything else is changing.

Code Example:

import React, { useCallback, useState } from 'react'

function LoyalFriend() {
  const [count, setCount] = useState(0)

  const increment = useCallback(() => {
    setCount(c => c + 1)
  }, [])

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </>
  )
}

useReducer: The Organized Toybox

What is it?

useReducer is like an organized toybox. It helps you manage lots of related things in one place.

Why is it important?

It makes it easier to handle complex state logic in your components.

When to use it?

Use it when you have multiple related state values or when the next state depends on the previous one.

Pros:

  • Centralizes state logic
  • Easier to test and debug

Cons:

  • Can be overkill for simple state management

Metaphor:

Think of useReducer as a toy sorter that helps you keep all your toys (or state) neatly organized and easy to find.

Code Example:

import React, { useReducer } from 'react'

const initialState = { count: 0 }

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 }
    case 'decrement':
      return { count: state.count - 1 }
    default:
      return state
  }
}

function OrganizedToybox() {
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    <>
      Count: {state.count}
      <button onClick={()=> dispatch({ type: 'increment' })}>+</button>
      <button onClick={()=> dispatch({ type: 'decrement' })}>-</button>
    </>
  )
}

useLayoutEffect: The Early Bird

What is it?

useLayoutEffect is like the early bird that catches the worm. It does things right after your website draws, but before it shows on the screen.

Why is it important?

It helps prevent flickering when you need to make visual changes.

When to use it?

Use it when you need to do something that affects the layout of your website immediately after rendering.

Pros:

  • Prevents visual flickering
  • Useful for DOM measurements

Cons:

  • Can slow down rendering if overused

Metaphor:

Imagine useLayoutEffect as a speedy painter who touches up your website's painting before anyone can see it.

Code Example:

import React, { useLayoutEffect, useRef, useState } from 'react'

function EarlyBird() {
  const [width, setWidth] = useState(0)
  const textRef = useRef()

  useLayoutEffect(() => {
    setWidth(textRef.current.getBoundingClientRect().width)
  }, [])

  return (
    <>
      <div ref={textRef}>Hello, World!</div>
      <p>The width is: {Math.round(width)}px</p>
    </>
  )
}

useImperativeHandle: The Secret Handshake

What is it?

useImperativeHandle is like a secret handshake. It lets you share special powers with your parent component.

Why is it important?

It gives you control over what a parent component can do with a child component.

When to use it?

Use it when you need to customize the instance value that's exposed to parent components when using ref.

Pros:

  • Provides fine-grained control over ref exposure
  • Useful for creating reusable components

Cons:

  • Can make component relationships more complex

Metaphor:

Think of useImperativeHandle as a special key that only lets certain people (parent components) unlock certain doors (functions) in your component.

Code Example:

import React, { useRef, useImperativeHandle, forwardRef } from 'react'

const SecretHandshake = forwardRef((props, ref) => {
  const inputRef = useRef()

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus()
    }
  }))

  return <input ref={inputRef} />
})

function Parent() {
  const childRef = useRef()

  return (
    <>
      <SecretHandshake ref={childRef} />
      <button onClick={()=> childRef.current.focus()}>Focus input</button>
    </>
  )
}

useDebugValue: The Magnifying Glass

What is it?

useDebugValue is like a magnifying glass for developers. It helps them see what's happening inside your custom hooks.

Why is it important?

It makes it easier to understand and debug custom hooks.

When to use it?

Use it in custom hooks when you want to display additional debugging information in React DevTools.

Pros:

  • Improves debugging experience
  • Helpful for complex custom hooks

Cons:

  • Only useful during development

Metaphor:

Imagine useDebugValue as a special sticker you put on your custom hooks that only other developers can see with their special developer glasses.

Code Example:

import React, { useState, useDebugValue } from 'react'

function useCount(initialCount = 0) {
  const [count, setCount] = useState(initialCount)

  useDebugValue(count > 0 ? 'Positive' : 'Zero or Negative')

  return [count, setCount]
}

function MagnifyingGlass() {
  const [count, setCount] = useCount(0)

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={()=> setCount(count + 1)}>Increment</button>
    </>
  )
}

Conclusion

We've explored all these amazing React Hooks. Each one is like a special superpower that helps you build awesome websites. Remember, just like with real superpowers, it's important to use them wisely.