0Pricing
Frontend Academy · บทเรียน

useReducer สำหรับสถานะที่ซับซ้อน

จำลองการเปลี่ยนสถานะด้วยฟังก์ชัน reducer ส่งออบเจ็กต์ action และเลือกใช้ useReducer แทน useState เมื่อสถานะมีตรรกะซับซ้อนขึ้น

useReducer สำหรับสถานะที่ซับซ้อน เป็นบทเรียน Frontend Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Frontend Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Frontend Academy มีบทเรียนทั้งหมด 4 บทเรียน

เมื่อ useState ไม่เพียงพอ

เมื่อการเปลี่ยนสถานะเกี่ยวข้องกับตรรกะที่ซับซ้อน ค่าหลายค่าที่สัมพันธ์กัน หรือเงื่อนไขที่สถานะใหม่ขึ้นอยู่กับสถานะก่อนหน้าในรูปแบบที่ไม่ตรงไปตรงมา useReducer จะเป็นตัวเลือกที่สะอาดกว่าการเรียกใช้ useState หลายครั้ง

รูปแบบฟังก์ชันรีดิวเซอร์

รีดิวเซอร์เป็นฟังก์ชันบริสุทธิ์: (state, action) => newState ฟังก์ชันนี้รับสถานะปัจจุบันและอ็อบเจ็กต์แอ็กชัน แล้วส่งคืนสถานะถัดไป ห้ามแก้ไขข้อมูลเดิม โดยต้องส่งคืนอ็อบเจ็กต์ใหม่เสมอ

type Action =
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'reset'; payload: number };

function counterReducer(state: number, action: Action): number {
  switch (action.type) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    case 'reset':     return action.payload;
    default: return state;
  }
}

ไวยากรณ์ของ useReducer

useReducer(reducer, initialState) จะคืนค่าสถานะปัจจุบันและฟังก์ชันสำหรับส่งคำสั่ง เรียกใช้ฟังก์ชันส่งคำสั่งพร้อมอ็อบเจ็กต์แอ็กชันเพื่อทำให้สถานะเปลี่ยน

function Counter() {
  const [count, dispatch] = useReducer(counterReducer, 0);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Reset</button>
    </div>
  );
}

แอ็กชันแบบมีชนิด — ยูเนียนแยกแยะได้

ใช้ชนิดยูเนียนแยกแยะได้ของ TypeScript สำหรับแอ็กชัน เพื่อให้ตัวตรวจสอบชนิดตรวจสอบทุกกรณีในโครงสร้าง switch ของรีดิวเซอร์ได้ครบถ้วน

type CartAction =
  | { type: 'ADD_ITEM'; item: CartItem }
  | { type: 'REMOVE_ITEM'; id: string }
  | { type: 'SET_QUANTITY'; id: string; qty: number }
  | { type: 'CLEAR' };

ตัวอย่างสถานะซับซ้อน: ตะกร้าสินค้า

ตะกร้าสินค้าที่มีการดำเนินการเพิ่ม ลบ และอัปเดตจำนวน เป็นกรณีใช้งาน useReducer ที่เหมาะอย่างยิ่ง

interface CartState {
  items: CartItem[];
  total: number;
}

function cartReducer(state: CartState, action: CartAction): CartState {
  switch (action.type) {
    case 'ADD_ITEM': {
      const exists = state.items.find(i => i.id === action.item.id);
      if (exists) {
        const items = state.items.map(i =>
          i.id === action.item.id ? { ...i, qty: i.qty + 1 } : i
        );
        return { ...state, items, total: calcTotal(items) };
      }
      const items = [...state.items, { ...action.item, qty: 1 }];
      return { ...state, items, total: calcTotal(items) };
    }
    case 'CLEAR': return { items: [], total: 0 };
    default: return state;
  }
}

ฟังก์ชันส่งคำสั่งมีความคงที่

ฟังก์ชัน dispatch มีความคงที่ ไม่เปลี่ยนระหว่างการเรนเดอร์ จึงส่งเป็นพร็อพหรือใช้เป็นการพึ่งพาของ useEffect ได้อย่างปลอดภัย โดยไม่ทำให้การทำงานซ้ำ

การรวม useReducer + useContext

รูปแบบที่ทรงพลังคือ ให้บริบททั้งสถานะและฟังก์ชันส่งคำสั่ง ทำให้คอมโพเนนต์ใด ๆ อ่านสถานะหรือส่งแอ็กชันได้โดยไม่ต้องส่งผ่านพร็อพหลายชั้น

const CartContext = createContext<{ state: CartState; dispatch: Dispatch<CartAction> } | null>(null);

function CartProvider({ children }: { children: React.ReactNode }) {
  const [state, dispatch] = useReducer(cartReducer, { items: [], total: 0 });
  return <CartContext.Provider value={{ state, dispatch }}>{children}</CartContext.Provider>;
}

useReducer เทียบกับ useState

เลือก useReducer เมื่อ: สถานะมีค่าย่อยหลายค่า สถานะถัดไปขึ้นอยู่กับสถานะก่อนหน้าในรูปแบบซับซ้อน การเปลี่ยนสถานะมีหลายแบบและตั้งชื่อไว้อย่างชัดเจน หรือคุณต้องการทดสอบตรรกะของสถานะแยกจากคอมโพเนนต์

การทดสอบรีดิวเซอร์แบบแยกอิสระ

เนื่องจากรีดิวเซอร์เป็นฟังก์ชันบริสุทธิ์ จึงทดสอบได้ง่ายมากโดยไม่ต้องเรนเดอร์คอมโพเนนต์ใด ๆ

test('increment action increases count', () => {
  expect(counterReducer(0, { type: 'increment' })).toBe(1);
  expect(counterReducer(5, { type: 'increment' })).toBe(6);
});

test('reset action sets count to payload', () => {
  expect(counterReducer(42, { type: 'reset', payload: 0 })).toBe(0);
});

Immer สำหรับการอัปเดตแบบไม่แก้ไขข้อมูลเดิม

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

ตรวจสอบความเข้าใจ

ลายเซ็นของฟังก์ชันรีดิวเซอร์คืออะไร

ทบทวน: useReducer

(สถานะ, แอ็กชัน) => newState — เป็นฟังก์ชันบริสุทธิ์และไม่แก้ไขข้อมูลเดิม useReducer(รีดิวเซอร์, ค่าเริ่มต้น) จะคืนค่า [สถานะ, ฟังก์ชันส่งคำสั่ง] ใช้ชนิดแอ็กชันแบบยูเนียนแยกแยะได้เพื่อความปลอดภัย ฟังก์ชันส่งคำสั่งมีความคงที่ เหมาะสำหรับสถานะซับซ้อนที่มีการเปลี่ยนหลายรูปแบบ ใช้ร่วมกับ useContext เพื่อจัดการสถานะส่วนกลาง ทดสอบรีดิวเซอร์แยกต่างหากในฐานะฟังก์ชันบริสุทธิ์

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

บทเรียน “useReducer สำหรับสถานะที่ซับซ้อน” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “useReducer สำหรับสถานะที่ซับซ้อน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Frontend Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Frontend Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “useReducer สำหรับสถานะที่ซับซ้อน”

จำลองการเปลี่ยนสถานะด้วยฟังก์ชัน reducer ส่งออบเจ็กต์ action และเลือกใช้ useReducer แทน useState เมื่อสถานะมีตรรกะซับซ้อนขึ้น คุณปฏิบัติ Frontend Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Frontend Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Frontend Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “useReducer สำหรับสถานะที่ซับซ้อน” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Frontend Academy นี้ได้ไหม

ได้ บทเรียน Frontend Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. useContext สำหรับสถานะส่วนกลาง
  2. useReducer สำหรับสถานะที่ซับซ้อน
  3. useMemo และ useCallback เพื่อประสิทธิภาพ
  4. Custom Hooks: แยกตรรกะที่นำกลับมาใช้ซ้ำได้
← กลับไปที่ Frontend Academy