React Hooks — มันคืออะเร็อ ฮูกหลักและกฎการใช้งาน

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-07-04 เวลาอ่าน: 10 นาที

React Hooks คือฟังก์ชันที่ช่วยให้คุณสามารถใช้ state, effect, context และคุณสมบัติอื่นๆ ของ React ในคอมโพนานท์แบบฟังก์ชัน โดยไม่ต้องเขียนคลาส แนะนำใน React 16.8 ฮูกได้เปลี่ยนแปลงแนวทางการพัฒนาอย่างสิ้นเชิง แทนทีรูปแบบที่ซับซ้อนอย่าง HOC และ render props ตามข้อมูลจาก React, 2024 มี 15 ฮูกในตัวที่ครอบคลุมทุกสถานการณ์การจัดการคอมโพนานท์

หัวข้อสำคัญ

  • ฮูก — ฟังก์ชันสำหรับเชื่อมต่อคุณสมบัติของ React ไปยังคอมโพนานท์แบบฟังก์ชันโดยไม่ต้อง extends Component
  • useState — ฮูกพื้นฐานสำหรับการจัดการ state คืนค่าและ setter
  • useEffect — สำหรับผลกระทบข้างค้าง: คำขอ API, การสมัครสมาชิก, ตัวจับเวลา, การจัดการ DOM
  • useContext — สำหรับเข้าถึง context ของ React โดยไม่ต้องใช้ Consumer wrapper และ static contextType
  • กฎของฮูก — เรียกใช้เฉพาะในระดับบนสุด และเฉพาะภายในคอมโพนานท์แบบฟังก์ชันหรือคสต้มฮูก

React Hooks คืออะไร?

React Hooks เป็นชุดของฟังก์ชันที่สร้างอยู่ใน React ที่ช่วยให้คุณสามารถเชื่อมต่อกับกลไกภายในเฟรมเวิร์กได้โดยไม่ต้องสร้างคลาส ก่อน React 16.8 คอมโพนานท์แบบฟังก์ชันสามารถรับ props และคืน JSX เท่านั้น — ตรรกะที่ซับซ้อนใดๆ ต้องใช้คอมโพนานท์แบบคลาส

ปัญหาของคอมโพนานท์แบบคลาสคือ การแยกตรรกะที่เกี่ยวข้องกัน โค้ดที่เกี่ยวข้องกับหนึ่งหน่วยงาน (เช่น การสมัครสมาชิก WebSocket) ถูกบังคับให้แยกระหว่าง componentDidMount, componentDidUpdate และ componentWillUnmount ฮูกแก้ไขปัญหานี้: ตรรกะทั้งหมดถูกจับกลุ่มอยู่ใน useEffect เดียว

ปัญหาที่สองคือ การนำตรรกะมาใช้ใหม่ เพื่อนำตรรกะ state มาใช้ใหม่ระหว่างคอมโพนานท์แบบคลาส คุณต้องใช้ HOC (คอมโพนานท์ระดับสูง) หรือ render props — ทั้งสองรูปแบบสร้างการซ้อนกันพิเศษในต้นไม้ของคอมโพนานท์ คสต้มฮูกแก้ไขปัญหานี้โดยไม่ต้องซ้อนกัน

useState — การจัดการ state

useState เป็นฮูกพื้นฐานที่สุด มันรับค่าเริ่มต้นและคืนค่าทูเปิ้ล: ค่าปัจจุบันและฟังก์ชันสำหรับอัพเดตมัน แตกต่างจาก this.setState ในคลาส useState ไม่รวมวัตถุ — state ใหม่แทนที่ state ก่อนหน้าอย่างสมบูรณ์สำหรับการเรียกใช้ useState เฉพาะครั้ง

js
import React, { useState } from 'react';

function Form() {
  const [text, setText] = useState('');
  const [submitting, setSubmitting] = useState(false);

  async function handleSubmit() {
    setSubmitting(true);
    await fetch('/api/submit', {
      method: 'POST',
      body: JSON.stringify({ text })
    });
    setSubmitting(false);
  }

  return (
    <div>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button onClick={handleSubmit}
        disabled={submitting}>
        {submitting ? 'Saving...' : 'Submit'}
      </button>
    </div>
  );
}

รูปแบบฟังก์ชันของ setState

setter ของ useState สามารถรับฟังก์ชันที่รับค่าก่อนหน้าและคืนค่าใหม่ รูปแบบฟังก์ชัน เป็นสิ่งจำเป็นเมื่อ state ใหม่คำนวณจาก state ก่อนหน้าและสามารถเรียก setState แบบเป็นชุดได้ — แต่ละครั้งจะได้รับค่าก่อนหน้าปัจจุบัน

useEffect — ผลกระทบข้างค้างและวงจรของชีวิต

useEffect เป็นฮูกสำหรับการทำผลกระทบข้างค้างในคอมโพนานท์แบบฟังก์ชัน มันรับอาร์กิวเมนสองตัว: ฟังก์ชันผลกระทบและอาร์เรย์การพึ่งพา React จะทำงานผลกระทปหลังแต่ละครั้งที่เรนเดอร์ แต่เมื่อระบุการพึ่งพา — เฉพาะเมื่อตัวใดตัวหนึ่งเปลี่ยนแปลง

อาร์เรย์การพึ่งพาที่ว่างเปล่า [] หมายความว่าผลกระทบจะทำงานหนึ่งครั้งหลังการเรนเดอร์ครั้งแรก (คล้ายกับ componentDidMount) การคืนค่าฟังก์ชันจากผลกระทบคือการทำความสะอาดที่ทำงานเมื่อ unmount (คล้ายกับ componentWillUnmount) และก่อนที่จะทำงานผลกระทบอีกครั้ง

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

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = new WebSocket(`wss://chat/${roomId}`);

    socket.onmessage = (event) => {
      setMessages(prev => [...prev, JSON.parse(event.data)]);
    };

    // ทำความสะอาด - ปิด socket
    return () => socket.close();
  }, [roomId]);

  return <ul>
    {messages.map((msg, i) =>
      <li key={i}>{msg.text}</li>
    )}
  </ul>;
}

การพึ่งพาของ useEffect เป็น ข้อมุมต่อที่เดียว ระหว่างผลกระทบและโลกภายนอก หากผลกระทบใช้ตัวแปรจาก props หรือ state ตัวแปรนั้นต้องอยู่ในอาร์เรย์การพึ่งพา การละเลยกฎนี้เป็นแหล่งของข้อผิดพลาดกับ stale closure

useContext — การเข้าถึง context

useContext เป็นฮูกสำหรับการอ่านค่า context มันรับวัตถุ context ที่สร้างโดย React.createContext และคืนค่าปัจจุบัน หาก context เปลี่ยนแปลง คอมโพนานท์จะเรนเดอร์ใหม่โดยอัตโนมัติด้วยค่าใหม่

ก่อน useContext การเข้าถึง context ในคอมโพนานท์แบบฟังก์ชันต้องห่อใน Context.Consumer ซึ่งทำให้เกิดการซ้อนกัน useContext กำจัด Consumer อย่างสมบูรณ์ — เพียงเรียกใช้ฮูกที่ใดก็ได้ในคอมโพนานท์แบบฟังก์ชัน

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

const ThemeContext = createContext('สว่าง');

function ThemedButton() {
  const theme = useContext(ThemeContext);

  return <button
    className={`btn btn-${theme}`}
  >
    {theme === 'dark' ? 'moon' : '☀️'}
  </button>;
}

useContext ไม่ใช่การแทนที่ Redux หรือ state ทั่วโลก React แนะนำให้ใช้ context สำหรับค่าที่เปลี่ยนแปลงไม่บ่อย — เครื่องหมาย, ภาษา, สถานะการตรวจสอบสิทธิ์ สำหรับข้อมูลที่อัพเดตบ่อยครั้ง แต่ละครั้งที่เรนเดอร์ใหม่จะคำนวณผู้ใช้ context ทั้งหมดใหม่ ซึ่งอาจทำให้ประสิทธิภาพลดลง

useReducer — ตรรกะ state ที่ซับซ้อน

useReducer เป็นทางเลือกของ useState สำหรับ state ที่มีตรรกะการอัพเดตที่ซับซ้อน มันรับฟังก์ชัน reducer และ state เริ่มต้น คืนค่า state ปัจจุบันและฟังก์ชัน dispatch reducer รับ state ปัจจุบันและ action แล้วคืนค่า state ใหม่ — เป็นฟังก์ชันบริสุทธิ์ที่ไม่มีผลกระทบข้างค้าง

useReducer มีประโยชน์โดยเฉพาะเมื่อหลาย action (set, reset, increment, addItem) จัดการ state แบบเดียว แทนที่จะใช้ useState หลายอันกับ setter ที่แตกต่างกัน — ใช้ reducer เดียวกับประเภท action ที่ชัดเจน ทำให้ตรรกะสามารถคาดการณ์ได้และทดสอบได้ง่าย

js
import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 };
    case 'setStep':
      return { ...state, step: action.step };
    case 'reset':
      return { count: 0, step: 1 };
    default:
      throw new Error('Unknown action: ' + action.type);
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, {
    count: 0,
    step: 1
  });

  return (
    <>
      <p>Count: {state.count}</p>
      <button
        onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button
        onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </>
  );
}

useRef, useMemo, useCallback — การเพิ่มประสิทธิภาพ

useRef สร้างวัตถุที่สามารถเปลี่ยนแปลงได้ซึ่งคงอยู่ระหว่างการเรนเดอร์ การเปลี่ยนแปลง current ไม่ทำให้เกิดการเรนเดอร์ใหม่ การใช้งานหลักคือการเข้าถึงองค์ประกอบ DOM และการเก็บค่าที่สามารถเปลี่ยนแปลงได้ (ตัวจับเวลา, ค่าก่อนหน้า, อินสแตนซ์ไลบรารี)

useMemo จำผลลัพธ์ของฟังก์ชัน มันจะคำนวณค่าใหม่เฉพาะเมื่อการพึ่งพาเปลี่ยนแปลง ใช้สำหรับการคำนวณที่มีราคาแพง — การกรองอาร์เรย์ขนาดใหญ่, การเรียงลำดับ, การจัดรูปแบบข้อมูล useCallback เป็นกรณีพิเศษของ useMemo สำหรับการจำผลลัพธ์ฟังก์ชัน

js
import React, { useRef, useMemo, useCallback } from 'react';

function SearchPage({ items, query, onItemSelect }) {
  const inputRef = useRef(null);

  // โฟกัสอัตโนมัติเมื่อ mount
  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  // จำผลลัพธ์ที่กรองแล้ว
  const filtered = useMemo(() => {
    return items.filter(item =>
      item.name.toLowerCase()
        .includes(query.toLowerCase())
    );
  }, [items, query]);

  // จำผลลัพธ์ callback
  const handleSelect = useCallback((id) => {
    onItemSelect(id);
  }, [onItemSelect]);

  return (
    <div>
      <input ref={inputRef}
        value={query}
        onChange={e => setQuery(e.target.value)}
      />
      <ul>
        {filtered.map(item =>
          <li key={item.id}>
            <button
              onClick={() => handleSelect(item.id)}>
              {item.name}
            </button>
          </li>
        )}
      </ul>
    </div>
  );
}

กฎที่สำคัญ: useRef, useMemo และ useCallback เป็น เครื่องมือเพิ่มประสิทธิภาพ ไม่ใช่โครงสร้างที่จำเป็น อย่าห่อทุกอย่างใน useMemo — วัดประสิทธิภาพก่อน การเพิ่มประสิทธิภาพก่อนกำหนดทำให้โค้ดซับซ้อน React เรนเดอร์คอมโพนานท์ได้อย่างรวดเร็ว; useMemo จำเป็นเฉพาะเมื่อการคำนวณมีราคาแพงจริง (O(n²) ขึ้นไป)

กฎของฮูกและข้อผิดพลาดที่พบบ่อย

React ได้แนะนำกฎที่เข้มงวดสองข้อสำหรับฮูก ข้อแรก: เรียกใช้ฮูกเฉพาะในระดับบนสุด — ไม่ใช้ภายในเงื่อนไข, ลูป หรือฟังก์ชันซ้อนกัน ข้อสอง: เรียกใช้ฮูกเฉพาะจากคอมโพนานท์แบบฟังก์ชันของ React หรือคสต้มฮูก ปลั๊กอิน ESLint eslint-plugin-react-hooks จะตรวจสอบทั้งสองกฎโดยอัตโนมัติ

ข้อผิดพลาดที่พบบ่อยที่สุดคือ การละเมิดลำดับของการเรียกใช้ฮูก React อึงพึ่งลำดับของการเรียกใช้ฮูกระหว่างการเรนเดอร์ หากฮูกอยู่ภายในเงื่อนไขและไม่ทำงานในการเรนเดอร์ครั้งใดเรื่องหนึ่ง ฮูกที่ต่อไปทั้งหมดจะเลื่อนทำให้เกิดข้อผิดพลาด

ข้อผิดพลาดสิ่งที่เกิดขึ้นวิธีแก้
ฮูกในเงื่อนไขลำดับของฮูกหักย้ายตรรกะเงื่อนไขไปไว้ในฮูก
Stale closureผลกระทบใช้ค่าเก่าเพิ่มการพึ่งพาทั้งหมดในอาร์เรย์
ลูปไม่สิ้นสุดuseEffect โดยไม่มีการพึ่งพาเปลี่ยน stateระบุการพึ่งพาหรือใช้ useReducer
การเปลี่ยนแปลง ref ใน renderผลกระทบข้างค้างในตัวฟังก์ชันย้ายการเปลี่ยนแปลงไปไว้ใน useEffect

ข้อผิดพลาดที่พบบ่อยอันดับสองคือ stale closure ใน useEffect หากผลกระทบใช้ตัวแปรที่ไม่ได้ระบุในการพึ่งพา ผลกระทบจะเก็บค่าของมันไว้ในขณะที่สร้าง closure วิธีแก้คือรวมตัวแปรที่ใช้ทั้งหมดไว้ในอาร์เรย์การพึ่งพาเสมอ หากมีการพึ่งพามากเกินไป ให้พิจารณาแยกผลกระทปออกเป็นหลายส่วน

คำถามที่พบบ่อย

ทำไมจึงไม่สามารถเรียกใช้ฮูกภายในเงื่อนไขได้?

React อึงพึ่งลำดับของการเรียกใช้ฮูก ระหว่างการเรนเดอร์ หากฮูกถูกเรียกใช้ภายในเงื่อนไขและไม่ทำงานในการเรนเดอร์ครั้งใดเรื่องหนึ่ง ฮูกทั้งหมดที่ต่อไปจะเลื่อนที่ — state ของมันปนกันไปมา กฎ ESLint hooks/exhaustive-deps เตือนเกี่ยวกับเรื่องนี้

ความแตกต่างระหว่าง useEffect และ useLayoutEffect คืออะไร?

useLayoutEffect ทำงานแบบซิงโครนัสและจะทำงานก่อนที่เบราว์เซอร์จะแสดงผลการเปลี่ยนแปลง useEffect ทำงานแบบญาสิงโครนัส — หลังการแสดงผล ใช้ useLayoutEffect เมื่อคุณต้องการวัดหรือเปลี่ยนแปลง DOM ก่อนที่ผู้ใช้จะเห็นการเปลี่ยนแปลง (ตำแหน่ง popover, การคืนสภาพการเลื่อนหน้าจอ)

วิธียกเลิกคำขอ fetch เมื่อ unmount?

ใช้ AbortController ภายใน useEffect สร้าง controller แล้วส่ง signal ไปยัง fetch และเรียก controller.abort() ในฟังก์ชันทำความสะอาด สำหรับเบราว์เซอร์รุ่นเก่า ใช้แฟลก cancelled: return () => { cancelled = true; } โดยมีการตรวจสอบก่อน setState

คสต้มฮูกคืออะไรและเมื่อใดควรสร้างมัน?

คสต้มฮูก คือฟังก์ชัน JavaScript ที่มีคำนำหน้า use และใช้งานฮูกในตัว สร้างเมื่อตรรกะเดียวกันของฮูกถูกใช้ซ้ำในสองคอมโพนานท์หรือมากกว่านั้น ตัวอย่าง: useDebounce, useLocalStorage, useMediaQuery คสต้มฮูกแทนที่ HOC และ render props

ทำไม useEffect ทำงานสองครั้งใน Strict Mode?

ใน Strict Mode React จะทำการ mount และ unmount คอมโพนานท์สองครั้งโดยเจตนาในโหมดการพัฒนาเพื่อค้นหาข้อผิดพลาดที่เกี่ยวกับผลกระทบที่ไม่ได้ทำความสะอาด หากการทำความสะอาดเขียนถูกต้อง การเรียกสองครั้งจะไม่ทำให้เกิดปัญหา สิ่งนี้ไม่เกิดขึ้นในโหมดการผลิต

สรุป

  • React Hooks — ฟังก์ชันสำหรับใช้ state, effect และ context ในคอมโพนานท์แบบฟังก์ชัน แทนที่คลาส
  • useState — ฮูกพื้นฐานสำหรับ state เฉพาะที่มี setter แบบฟังก์ชันสำหรับการอัพเดตที่ปลอดภัย
  • useEffect — ฮูกสำหรับผลกระทบข้างค้างที่มีอาร์เรย์การพึ่งพาและฟังก์ชันทำความสะอาด
  • useContext — การเข้าถึง context ของ React โดยตรงโดยไม่ต้องใช้ Consumer wrapper และการซ้อนกันพิเศษ
  • useReducer — สำหรับตรรกะ state ที่ซับซ้อนด้วยประเภท action เป็นทางเลือกของ Redux ภายในคอมโพนานท์เดียว
  • useRef, useMemo, useCallback — เครื่องมือเพิ่มประสิทธิภาพ: การอ้างอิง DOM, การจำค่าและ callback
  • กฎระดับบนสุด — ข้อจำกัดที่เข้มงวดในการเรียกใช้ฮูกนอกเงื่อนไข, ลูป และฟังก์ชันที่ซ้อนกัน

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม