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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- useContext สำหรับสถานะส่วนกลาง
- useReducer สำหรับสถานะที่ซับซ้อน
- useMemo และ useCallback เพื่อประสิทธิภาพ
- Custom Hooks: แยกตรรกะที่นำกลับมาใช้ซ้ำได้