back
May 9, 2026

React Hooks (Less Common, But Useful)

บันทึก React Hooks ที่ไม่ได้ใช้บ่อยเท่า useState กับ useEffect แต่พอถึงเวลาที่ต้องใช้แล้วมีประโยชน์มาก (หรือบางทีก็ลืม Syntax)


useRef

เอาไว้อ้างอิง (Reference) ถึง DOM Element ตรงๆ หรือเอาไว้เก็บค่าบางอย่างที่เปลี่ยนไปแล้วจะไม่ทำให้ Component Re-render

tsx
import { useRef, useEffect } from "react"

function AutoFocusInput() {
  const inputRef = useRef<HTMLInputElement>(null)

  // ใช้เก็บค่าที่แก้แล้วไม่เกิด Re-render
  const renderCount = useRef(0)

  useEffect(() => {
    // อ้างอิงถึง DOM เพื่อสั่ง focus
    inputRef.current?.focus()
    renderCount.current += 1
  }, [])

  return <input ref={inputRef} placeholder="พิมพ์ที่นี่..." />
}

useMemo

เอาไว้จำค่าที่คำนวณยากๆ (Expensive Computation) จะได้ไม่ต้องคำนวณใหม่ทุกครั้งที่ Component Re-render จะคำนวณใหม่ก็ต่อเมื่อ Dependency เปลี่ยนเท่านั้น

tsx
import { useMemo, useState } from "react"

function FilterList({ items, filterText }) {
  // สมมติว่าการ filter ใช้พลังประมวลผลเยอะมาก
  const filteredItems = useMemo(() => {
    console.log("Filtering items...")
    return items.filter((item) => item.name.includes(filterText))
  }, [items, filterText]) // เปลี่ยนเมื่อ items หรือ filterText เปลี่ยนเท่านั้น

  return (
    <ul>
      {filteredItems.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  )
}

useCallback

คล้ายๆ useMemo แต่แทนที่จะจำ "ค่า" มันเอาไว้จำ "ฟังก์ชัน" มักใช้คู่กับ React.memo เวลาส่งฟังก์ชันไปให้ Component ลูก เพื่อป้องกันไม่ให้ Component ลูก Re-render ฟรีๆ

tsx
import { useCallback, useState } from "react"

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

  // ถ้าไม่ใส่ useCallback ฟังก์ชันนี้จะถูกสร้างใหม่ทุกครั้งที่ Parent Re-render
  const handleSubmit = useCallback((data) => {
    console.log("Submit:", data)
  }, []) // ไม่มี Dependency แปลว่าจำฟังก์ชันนี้ตลอดไป

  return <ChildComponent onSubmit={handleSubmit} />
}

useReducer

เอาไว้จัดการ State ที่มีความซับซ้อน หรือมีหลาย Action ที่เกี่ยวข้องกัน (คล้ายๆ Redux แต่ใช้แค่ใน Component ตัวเอง)

tsx
import { useReducer } from "react"

// 1. สร้าง Reducer Function
function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 }
    case "decrement":
      return { count: state.count - 1 }
    case "reset":
      return { count: 0 }
    default:
      return state
  }
}

function Counter() {
  // 2. เรียกใช้
  const [state, dispatch] = useReducer(reducer, { count: 0 })

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
      <button onClick={() => dispatch({ type: "reset" })}>Reset</button>
    </div>
  )
}

useLayoutEffect

ทำงานคล้าย useEffect เลย แต่มันจะทำงานก่อนที่เบราว์เซอร์จะเพนต์จอ (Paint) ให้ผู้ใช้เห็น เหมาะสำหรับการวัดขนาดกล่อง (DOM Measurement) เพื่อป้องกันหน้าจอกะพริบ (Flicker)

ข้อควรระวัง: ใช้เฉพาะตอนที่จำเป็นจริงๆ เท่านั้น เพราะมันจะ Block การวาดหน้าจอ ทำให้เว็บดูช้าลง

tsx
import { useLayoutEffect, useRef, useState } from "react"

function Tooltip() {
  const boxRef = useRef<HTMLDivElement>(null)
  const [height, setHeight] = useState(0)

  useLayoutEffect(() => {
    // วัดขนาดให้เสร็จก่อนวาดขึ้นจอ
    if (boxRef.current) {
      setHeight(boxRef.current.getBoundingClientRect().height)
    }
  }, [])

  return <div ref={boxRef}>Box Height: {height}</div>
}

useTransition

เอาไว้บอก React ว่าการอัปเดต State ครั้งนี้ "รอก่อนได้" (Non-urgent) ไม่ต้องรีบทำทันที ปล่อยให้หน้าจอแสดงผลอย่างอื่นไปก่อน (เช่น ให้ผู้ใช้พิมพ์ต่อได้ ไม่ค้าง)

tsx
import { useState, useTransition } from "react"

function Search() {
  const [isPending, startTransition] = useTransition()
  const [query, setQuery] = useState("")

  const handleChange = (e) => {
    // การอัปเดตที่อยากให้ทำทันที
    setQuery(e.target.value)

    // การอัปเดตที่กินพลังงานเยอะ และ "รอได้"
    startTransition(() => {
      // เรียกฟังก์ชันค้นหาที่ใช้เวลานาน
      performHeavySearch(e.target.value)
    })
  }

  return (
    <div>
      <input value={query} onChange={handleChange} />
      {isPending && <p>Loading results...</p>}
    </div>
  )
}