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)