useReducer
useReducer คือ Hook ที่ใช้จัดการ state แบบที่มี หลายสถานะ หรือ logic ซับซ้อน
คล้าย Redux ย่อส่วน ใช้คู่กับ dispatch และ reducer function
ตัวอย่างง่าย ๆ: Counter
import { useReducer } from 'react'; function counterReducer(state: number, action: { type: string }) { switch (action.type) { case 'increment': return state + 1; case 'decrement': return state - 1; default: return state; } } export default function Counter() { const [count, dispatch] = useReducer(counterReducer, 0); return ( <div> <p>Count: {count}</p> <button onClick={() => dispatch({ type: 'increment' })}>เพิ่ม</button> <button onClick={() => dispatch({ type: 'decrement' })}>ลด</button> </div> ); }
โครงสร้างจำง่าย:
const [state, dispatch] = useReducer(reducerFunction, initialState);
- state → ค่าปัจจุบัน
- dispatch(action) → ส่ง action ไปให้ reducer
- reducer(state, action) → ฟังก์ชันที่กำหนดว่า state จะเปลี่ยนยังไง
ใช้เมื่อไหร่ดี?
- ต้องจัดการ state ที่มีหลายประเภท เช่น เพิ่ม, ลบ, รีเซ็ต
- ต้องการแยก logic ออกจาก component (เช่น form, cart, etc.)
- ไม่อยากใช้ Redux แต่ logic คล้ายกัน
ประกาศประเภท State และ Action
type State = { count: number; showCount: boolean; }; type Action = | { type: 'increment' } | { type: 'decrement' } | { type: 'reset' } | { type: 'toggle' };
เขียน reducer ฟังก์ชัน
function reducer(state: State, action: Action): State { switch (action.type) { case 'increment': return { ...state, count: state.count + 1 }; case 'decrement': return { ...state, count: state.count - 1 }; case 'reset': return { ...state, count: 0 }; case 'toggle': return { ...state, showCount: !state.showCount }; default: return state; } }
ใช้งานในคอมโพเนนต์
import React, { useReducer } from 'react'; const initialState: State = { count: 0, showCount: true, }; export default function CounterReducer() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <h2>useReducer แบบ Object State + TypeScript</h2> <button onClick={() => dispatch({ type: 'toggle' })}> {state.showCount ? 'ซ่อน' : 'แสดง'} ตัวเลข </button> {state.showCount && <p>Count: {state.count}</p>} <button onClick={() => dispatch({ type: 'increment' })}>เพิ่ม</button> <button onClick={() => dispatch({ type: 'decrement' })}>ลด</button> <button onClick={() => dispatch({ type: 'reset' })}>รีเซ็ต</button> </div> ); }
สรุปจำง่าย
ส่วนประกอบ | ทำหน้าที่อะไร |
---|---|
State | โครงสร้างข้อมูลที่เราจะเก็บ |
Action | ชนิดของคำสั่งที่ส่งเข้า reducer |
reducer() | ฟังก์ชันหลักที่กำหนดว่า state เปลี่ยนยังไง |
dispatch() | ใช้ส่ง action เข้า reducer |
Redux
Redux แบบดั้งเดิม (Vanilla Redux) เพื่อความเข้าใจ หลักการพื้นฐานจริง ๆ ก่อนใช้ Redux Toolkit
เราจะมาเริ่มจากโค้ดพื้นฐานที่ใช้แนวคิด action → reducer → store → dispatch ค่ะ
ติดตั้ง redux และ react-redux
npm install redux react-redux
สร้าง Action Type และ Action Creator
// action types export const INCREMENT = 'INCREMENT'; export const DECREMENT = 'DECREMENT'; export const RESET = 'RESET'; // action creators export const increment = () => ({ type: INCREMENT }); export const decrement = () => ({ type: DECREMENT }); export const reset = () => ({ type: RESET });
สร้าง Reducer
src/redux/reducer.js
import { INCREMENT, DECREMENT, RESET } from './actions'; const initialState = { count: 0, }; export const counterReducer = (state = initialState, action) => { switch (action.type) { case INCREMENT: return { ...state, count: state.count + 1 }; case DECREMENT: return { ...state, count: state.count - 1 }; case RESET: return { ...state, count: 0 }; default: return state; } };
สร้าง Store
import { createStore } from 'redux'; import { counterReducer } from './reducer'; export const store = createStore(counterReducer);
ครอบ ด้วย
src/main.jsx (หรือ index.js)
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { Provider } from 'react-redux'; import { store } from './redux/store'; ReactDOM.createRoot(document.getElementById('root')).render( <Provider store={store}> <App /> </Provider> );
ใช้ใน Component
src/App.jsx
import { useSelector, useDispatch } from 'react-redux'; import { increment, decrement, reset } from './redux/actions'; export default function App() { const count = useSelector(state => state.count); const dispatch = useDispatch(); return ( <div> <h1>Counter: {count}</h1> <button onClick={() => dispatch(increment())}>เพิ่ม</button> <button onClick={() => dispatch(decrement())}>ลด</button> <button onClick={() => dispatch(reset())}>รีเซ็ต</button> </div> ); }
สรุปโครงสร้าง Redux ดั้งเดิม
ส่วนประกอบ | หน้าที่ |
---|---|
action | สิ่งที่บอกว่า "จะทำอะไร" (type: 'INCREMENT') |
reducer | รับ state + action → คืนค่า state ใหม่ |
store | ที่รวม state และจัดการ dispatch, subscribe ฯลฯ |
dispatch() | ส่ง action ไปให้ reducer |
Provider | ครอบแอปเพื่อให้ทุก component ใช้ store ได้ |
useSelector() | ดึงค่า state จาก store |
useDispatch() | ส่ง action เข้าสู่ store |
Redux Toolkit (RTK)
เริ่มต้นใช้ Redux ง่าย ๆ แนะนำให้เริ่มจาก Redux Toolkit (RTK) ซึ่งเป็นวิธีที่ทีม Redux แนะนำให้ใช้เป็นหลักในปัจจุบัน เพราะลดความซับซ้อนและโค้ดเยอะจาก Redux แบบดั้งเดิม
เป้าหมาย: "สร้าง Counter App ด้วย Redux Toolkit"
ขั้นตอนง่าย ๆ (แบบ Step-by-Step)
ติดตั้ง Redux Toolkit + React Redux
npm install @reduxjs/toolkit react-redux
สร้าง store
src/store.ts
import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './counterSlice'; export const store = configureStore({ reducer: { counter: counterReducer, }, }); // สำหรับใช้ใน TypeScript export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch;
สร้าง slice
src/counterSlice.ts
import { createSlice } from '@reduxjs/toolkit'; const counterSlice = createSlice({ name: 'counter', initialState: { value: 0, }, reducers: { increment: (state) => { state.value += 1; }, decrement: (state) => { state.value -= 1; }, reset: (state) => { state.value = 0; }, }, }); export const { increment, decrement, reset } = counterSlice.actions; export default counterSlice.reducer;
ครอบแอปด้วย Provider
src/main.tsx หรือ src/index.tsx
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { Provider } from 'react-redux'; import { store } from './store'; ReactDOM.createRoot(document.getElementById('root')!).render( <Provider store={store}> <App /> </Provider> );
ใช้ใน Component
src/App.tsx
import { useSelector, useDispatch } from 'react-redux'; import { RootState, AppDispatch } from './store'; import { increment, decrement, reset } from './counterSlice'; export default function App() { const count = useSelector((state: RootState) => state.counter.value); const dispatch: AppDispatch = useDispatch(); return ( <div> <h1>นับเลข: {count}</h1> <button onClick={() => dispatch(increment())}>เพิ่ม</button> <button onClick={() => dispatch(decrement())}>ลด</button> <button onClick={() => dispatch(reset())}>รีเซ็ต</button> </div> ); }
สรุปจำง่าย
สิ่งที่ทำ | ชื่อไฟล์ |
---|---|
สร้าง state + action | counterSlice.ts |
รวม reducer | store.ts |
ครอบ Provider | main.tsx |
ใช้งานในหน้า UI | App.tsx |
เคล็ดลับ
- RTK ใช้ Immer อยู่แล้ว → สามารถ "แก้ state ตรง ๆ" ได้
- ใช้ useSelector() เพื่ออ่านค่า
- ใช้ useDispatch() เพื่อเรียก action ไปเปลี่ยนค่า
Top comments (0)