Hôm nay tôi sẽ làm ví dụ Redux Thunk trong React theo kiểu dân dã, dễ hình dung như đi chợ mua cá vậy! Hãy tưởng tượng bạn đang xây một ứng dụng React để quản lý việc đi chợ, và Redux Thunk giúp bạn xử lý việc "đi mua cá" (gọi API) một cách gọn gàng.
Tình huống thực tế
Bạn có một ứng dụng "Đi Chợ". Người dùng nhấn nút "Mua cá", ứng dụng sẽ:
- Kiểm tra xem ở chợ có cá gì (gọi API để lấy danh sách cá).
- Hiển thị "Đang chạy ra chợ..." trong lúc chờ.
- Nếu chợ có cá, hiển thị danh sách: "Cá rô, cá lóc, cá thu...".
- Nếu chợ hết cá (lỗi API), hiển thị: "Hôm nay hết cá rồi!".
Redux Thunk sẽ giúp bạn quản lý việc "chạy ra chợ" (gọi API) và cập nhật danh sách cá một cách khoa học.
Ví dụ mã nguồn (dân dã)
1. Ý tưởng chính
- Store: Giống như cái tủ lạnh ở nhà, lưu danh sách cá và tình trạng đi chợ.
- Actions: Giống như việc bạn sai người đi chợ, báo cho họ biết cần làm gì.
- Thunk: Giống như người đi chợ, họ sẽ chạy ra chợ, kiểm tra cá, rồi mang về báo cáo.
- Reducer: Giống như bạn sắp xếp cá vào tủ lạnh sau khi nhận được.
2. Cấu trúc thư mục
src/
├── actions/
│ └── marketActions.js
├── reducers/
│ └── marketReducer.js
├── store.js
├── components/
│ └── FishMarket.js
├── App.js
3. Thiết lập tủ lạnh (store.js)
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import marketReducer from './reducers/marketReducer';
const store = createStore(marketReducer, applyMiddleware(thunk));
export default store;
4. Hành động đi chợ (marketActions.js)
// Loại hành động
const CHAY_RA_CHO = 'CHAY_RA_CHO'; // Đang chạy ra chợ
const MUA_CA_THANH_CONG = 'MUA_CA_THANH_CONG'; // Mua được cá
const HET_CA = 'HET_CA'; // Chợ hết cá
// Action creators: Báo trạng thái
const chayRaCho = () => ({
type: CHAY_RA_CHO,
});
const muaCaThanhCong = (fishList) => ({
type: MUA_CA_THANH_CONG,
payload: fishList,
});
const hetCa = (error) => ({
type: HET_CA,
payload: error,
});
// Thunk: Người đi chợ
import axios from 'axios';
export const muaCa = () => {
return (dispatch) => {
dispatch(chayRaCho()); // Báo: "Đang chạy ra chợ..."
// Giả lập gọi API ở chợ (dùng API miễn phí)
axios
.get('https://jsonplaceholder.typicode.com/users') // Tạm dùng API này, giả lập danh sách cá
.then((response) => {
// Giả lập danh sách cá từ dữ liệu API
const fishList = response.data.map((user, index) => ({
id: user.id,
name: `Cá ${index + 1}: ${user.name}`,
}));
dispatch(muaCaThanhCong(fishList)); // Mua được cá, báo về
})
.catch((error) => {
dispatch(hetCa("Hôm nay chợ hết cá rồi!")); // Lỗi, báo về
});
};
};
5. Sắp xếp tủ lạnh (marketReducer.js)
const initialState = {
dangChayRaCho: false, // Có đang chạy ra chợ không?
fishList: [], // Danh sách cá
error: '', // Lỗi (nếu có)
};
const marketReducer = (state = initialState, action) => {
switch (action.type) {
case 'CHAY_RA_CHO':
return {
...state,
dangChayRaCho: true,
error: '',
};
case 'MUA_CA_THANH_CONG':
return {
dangChayRaCho: false,
fishList: action.payload,
error: '',
};
case 'HET_CA':
return {
dangChayRaCho: false,
fishList: [],
error: action.payload,
};
default:
return state;
}
};
export default marketReducer;
6. Giao diện đi chợ (FishMarket.js)
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { muaCa } from '../actions/marketActions';
const FishMarket = () => {
const dispatch = useDispatch();
const { dangChayRaCho, fishList, error } = useSelector((state) => state);
const handleMuaCa = () => {
dispatch(muaCa()); // Sai người đi chợ
};
return (
<div>
<h2>Chợ Cá Tươi Sống</h2>
<button onClick={handleMuaCa}>Mua cá</button>
{dangChayRaCho && <p>Đang chạy ra chợ...</p>}
{error && <p>Ôi không: {error}</p>}
<ul>
{fishList.map((fish) => (
<li key={fish.id}>{fish.name}</li>
))}
</ul>
</div>
);
};
export default FishMarket;
7. Ứng dụng chính (App.js)
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import FishMarket from './components/FishMarket';
const App = () => {
return (
<Provider store={store}>
<FishMarket />
</Provider>
);
};
export default App;
Giải thích kiểu dân dã
- Nhấn nút "Mua cá":
- Bạn sai người đi chợ bằng cách gọi
muaCa
(Thunk action).
- Bạn sai người đi chợ bằng cách gọi
- Người đi chợ (Redux Thunk):
- Báo: "Đang chạy ra chợ..." (
chayRaCho
). - Tới chợ, kiểm tra hàng (gọi API).
- Nếu có cá, mang về danh sách cá (
muaCaThanhCong
). - Nếu chợ hết cá, báo lỗi: "Hôm nay hết cá rồi!" (
hetCa
).
- Báo: "Đang chạy ra chợ..." (
- Sắp xếp tủ lạnh (Reducer):
- Cập nhật tủ lạnh: đang chạy chợ, danh sách cá, hoặc lỗi.
- Cập nhật giao diện:
- Giao diện hiển thị trạng thái: "Đang chạy ra chợ...", danh sách cá, hoặc thông báo lỗi.
Kết quả trực quan
- Ban đầu: Giao diện có nút "Mua cá".
- Nhấn nút: Hiện "Đang chạy ra chợ...".
- API trả về: Hiển thị danh sách cá (ví dụ: "Cá 1: Cá rô, Cá 2: Cá lóc...").
- Nếu lỗi: Hiện "Ôi không: Hôm nay chợ hết cá rồi!".
Tại sao dùng Redux Thunk?
- Như người đi chợ thông minh: Thunk giúp xử lý việc "chạy ra chợ" (gọi API) mà không làm rối code trong component.
- Tủ lạnh gọn gàng: Reducer giữ trạng thái (danh sách cá, lỗi) rõ ràng, dễ quản lý.
- Dễ sai người đi tiếp: Có thể thêm các hành động khác như "Mua rau", "Mua thịt" mà không cần thay đổi cấu trúc.
Chạy thử
- Cài dependencies:
npm install redux react-redux redux-thunk axios
. - Copy code vào dự án React.
- Chạy
npm start
, nhấn nút "Mua cá" để thấy kết quả.
Ví dụ về thêm tính năng
Haha, đi chợ mà chỉ mua cá thì thiếu mất món ngon! Mình sẽ mở rộng ví dụ "Đi Chợ" dân dã trước đó, thêm tính năng mua rau và mua thịt vào ứng dụng React với Redux Thunk. Mục tiêu là giữ mọi thứ đơn giản, dễ hình dung, như kiểu sai người đi chợ mua thêm vài thứ cho bữa cơm chiều.
Tình huống
Ứng dụng "Đi Chợ" giờ không chỉ mua cá, mà còn:
- Mua rau: Lấy danh sách rau củ (cải, rau muống, bắp cải...).
- Mua thịt: Lấy danh sách thịt (thịt heo, thịt bò, thịt gà...).
- Mỗi lần nhấn nút "Mua cá", "Mua rau", hoặc "Mua thịt", ứng dụng sẽ:
- Hiển thị "Đang chạy ra chợ...".
- Gọi API (giả lập bằng
jsonplaceholder
) để lấy danh sách. - Cập nhật danh sách hàng tương ứng hoặc báo lỗi nếu "chợ hết hàng".
Redux Thunk sẽ giúp xử lý các chuyến đi chợ (gọi API) một cách gọn gàng.
Cấu trúc mã nguồn
Mình sẽ cập nhật code từ ví dụ trước, thêm logic cho rau và thịt. Dưới đây là các file chính, với những thay đổi để hỗ trợ mua rau và thịt.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Đi Chợ App</title>
<script src="https://cdn.jsdelivr.net/npm/react@18/umd/react.development.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/redux@4/dist/redux.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-redux@8/dist/react-redux.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/redux-thunk@2/dist/redux-thunk.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@1/dist/axios.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// store.js
const { createStore, applyMiddleware } = Redux;
const thunk = ReduxThunk.default;
// marketReducer.js
const initialState = {
dangChayRaCho: false,
fishList: [],
veggieList: [],
meatList: [],
error: '',
};
const marketReducer = (state = initialState, action) => {
switch (action.type) {
case 'CHAY_RA_CHO':
return {
...state,
dangChayRaCho: true,
error: '',
};
case 'MUA_CA_THANH_CONG':
return {
...state,
dangChayRaCho: false,
fishList: action.payload,
error: '',
};
case 'MUA_RAU_THANH_CONG':
return {
...state,
dangChayRaCho: false,
veggieList: action.payload,
error: '',
};
case 'MUA_THIT_THANH_CONG':
return {
...state,
dangChayRaCho: false,
meatList: action.payload,
error: '',
};
case 'HET_HANG':
return {
...state,
dangChayRaCho: false,
error: action.payload,
};
default:
return state;
}
};
const store = createStore(marketReducer, applyMiddleware(thunk));
// marketActions.js
const CHAY_RA_CHO = 'CHAY_RA_CHO';
const MUA_CA_THANH_CONG = 'MUA_CA_THANH_CONG';
const MUA_RAU_THANH_CONG = 'MUA_RAU_THANH_CONG';
const MUA_THIT_THANH_CONG = 'MUA_THIT_THANH_CONG';
const HET_HANG = 'HET_HANG';
const chayRaCho = () => ({
type: CHAY_RA_CHO,
});
const muaCaThanhCong = (fishList) => ({
type: MUA_CA_THANH_CONG,
payload: fishList,
});
const muaRauThanhCong = (veggieList) => ({
type: MUA_RAU_THANH_CONG,
payload: veggieList,
});
const muaThitThanhCong = (meatList) => ({
type: MUA_THIT_THANH_CONG,
payload: meatList,
});
const hetHang = (error) => ({
type: HET_HANG,
payload: error,
});
const muaCa = () => {
return (dispatch) => {
dispatch(chayRaCho());
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((response) => {
const fishList = response.data.map((user, index) => ({
id: user.id,
name: `Cá ${index + 1}: ${user.name}`,
}));
dispatch(muaCaThanhCong(fishList));
})
.catch((error) => {
dispatch(hetHang('Hôm nay chợ hết cá rồi!'));
});
};
};
const muaRau = () => {
return (dispatch) => {
dispatch(chayRaCho());
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((response) => {
const veggieList = response.data.map((user, index) => ({
id: user.id,
name: `Rau ${index + 1}: ${user.name}`,
}));
dispatch(muaRauThanhCong(veggieList));
})
.catch((error) => {
dispatch(hetHang('Hôm nay chợ hết rau rồi!'));
});
};
};
const muaThit = () => {
return (dispatch) => {
dispatch(chayRaCho());
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((response) => {
const meatList = response.data.map((user, index) => ({
id: user.id,
name: `Thịt ${index + 1}: ${user.name}`,
}));
dispatch(muaThitThanhCong(meatList));
})
.catch((error) => {
dispatch(hetHang('Hôm nay chợ hết thịt rồi!'));
});
};
};
// FishMarket.js
const FishMarket = () => {
const dispatch = ReactRedux.useDispatch();
const { dangChayRaCho, fishList, veggieList, meatList, error } = ReactRedux.useSelector((state) => state);
const handleMuaCa = () => dispatch(muaCa());
const handleMuaRau = () => dispatch(muaRau());
const handleMuaThit = () => dispatch(muaThit());
return (
<div className="p-6 max-w-lg mx-auto">
<h2 className="text-2xl font-bold mb-4">Chợ Tươi Sống</h2>
<div className="flex gap-2 mb-4">
<button onClick={handleMuaCa} className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">Mua Cá</button>
<button onClick={handleMuaRau} className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">Mua Rau</button>
<button onClick={handleMuaThit} className="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600">Mua Thịt</button>
</div>
{dangChayRaCho && <p className="text-gray-600">Đang chạy ra chợ...</p>}
{error && <p className="text-red-600">Ôi không: {error}</p>}
<div className="grid grid-cols-1 gap-4">
<div>
<h3 className="font-semibold">Danh sách Cá:</h3>
<ul className="list-disc pl-5">
{fishList.map((fish) => (
<li key={fish.id}>{fish.name}</li>
))}
</ul>
</div>
<div>
<h3 className="font-semibold">Danh sách Rau:</h3>
<ul className="list-disc pl-5">
{veggieList.map((veggie) => (
<li key={veggie.id}>{veggie.name}</li>
))}
</ul>
</div>
<div>
<h3 className="font-semibold">Danh sách Thịt:</h3>
<ul className="list-disc pl-5">
{meatList.map((meat) => (
<li key={meat.id}>{meat.name}</li>
))}
</ul>
</div>
</div>
</div>
);
};
// App.js
const App = () => {
return (
<ReactRedux.Provider store={store}>
<FishMarket />
</ReactRedux.Provider>
);
};
// Render
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>
Giải thích kiểu dân dã
- Nhấn nút "Mua Cá", "Mua Rau", hoặc "Mua Thịt":
- Bạn sai người đi chợ bằng cách gọi
muaCa
,muaRau
, hoặcmuaThit
(các Thunk action).
- Bạn sai người đi chợ bằng cách gọi
- Người đi chợ (Redux Thunk):
- Báo: "Đang chạy ra chợ..." (
chayRaCho
). - Tới chợ, kiểm tra hàng (gọi API, dùng
jsonplaceholder
giả lập). - Nếu có hàng, mang về danh sách cá/rau/thịt (
muaCaThanhCong
,muaRauThanhCong
,muaThitThanhCong
). - Nếu hết hàng, báo lỗi: "Hôm nay chợ hết [cá/rau/thịt] rồi!" (
hetHang
).
- Báo: "Đang chạy ra chợ..." (
- Sắp xếp tủ lạnh (Reducer):
- Cập nhật tủ lạnh: trạng thái chạy chợ, danh sách cá, rau, thịt, hoặc lỗi.
- Cập nhật giao diện:
- Hiển thị trạng thái "Đang chạy ra chợ...", danh sách cá/rau/thịt, hoặc thông báo lỗi.
Kết quả trực quan
- Ban đầu: Giao diện có 3 nút: "Mua Cá", "Mua Rau", "Mua Thịt".
- Nhấn nút "Mua Cá": Hiện "Đang chạy ra chợ...", rồi hiển thị danh sách cá (ví dụ: "Cá 1: Cá rô...").
- Nhấn nút "Mua Rau": Tương tự, hiển thị danh sách rau (ví dụ: "Rau 1: Rau muống...").
- Nhấn nút "Mua Thịt": Hiển thị danh sách thịt (ví dụ: "Thịt 1: Thịt heo...").
- Nếu lỗi: Hiện "Ôi không: Hôm nay chợ hết [cá/rau/thịt] rồi!".
Tại sao dùng Redux Thunk?
- Như người đi chợ đa năng: Thunk giúp xử lý nhiều chuyến đi chợ (cá, rau, thịt) mà không làm rối code.
- Tủ lạnh ngăn nắp: Reducer quản lý riêng danh sách cá, rau, thịt, dễ mở rộng.
- Dễ sai bảo thêm: Muốn mua thêm "trái cây" hay "nước mắm"? Chỉ cần thêm action và cập nhật reducer.
Chạy thử
- Tạo file
index.html
và dán code trong<xaiArtifact>
vào. - Mở file bằng trình duyệt (cần kết nối Internet để tải CDN).
- Nhấn các nút "Mua Cá", "Mua Rau", "Mua Thịt" để thấy kết quả.
Bảng tóm tắt về Actions, Reducer, và Action Creators
Dưới đây là bảng tóm tắt về Actions, Reducer, và Action Creators trong ví dụ ứng dụng "Đi Chợ" với Redux Thunk, giải thích rõ ràng theo kiểu dân dã để dễ hình dung. Mình sẽ dựa trên ví dụ trước (mua cá, mua rau, mua thịt) để lập bảng và giải thích vai trò của từng phần.
Bảng tóm tắt: Actions, Reducer, Action Creators
Thành phần | Tên | Mô tả dân dã | Ý nghĩa trong code |
---|---|---|---|
Action Types | CHAY_RA_CHO |
"Báo là đang chạy ra chợ mua đồ!" | Đặt trạng thái dangChayRaCho: true để hiển thị "Đang chạy ra chợ..." trên giao diện. |
MUA_CA_THANH_CONG |
"Mua được cá, mang về đây!" | Cập nhật danh sách cá (fishList ) vào store và xóa lỗi. |
|
MUA_RAU_THANH_CONG |
"Mua được rau, tươi lắm nè!" | Cập nhật danh sách rau (veggieList ) vào store và xóa lỗi. |
|
MUA_THIT_THANH_CONG |
"Mua được thịt, ngon khỏi chê!" | Cập nhật danh sách thịt (meatList ) vào store và xóa lỗi. |
|
HET_HANG |
"Ôi, chợ hết hàng rồi!" | Cập nhật thông báo lỗi (error ) và xóa danh sách hàng tương ứng. |
|
Action Creators | chayRaCho() |
"Hô lên: Tao đi chợ đây!" | Trả về action { type: CHAY_RA_CHO } để báo trạng thái đang đi chợ. |
muaCaThanhCong(fishList) |
"Mang cá về, đây là danh sách!" | Trả về action { type: MUA_CA_THANH_CONG, payload: fishList } với danh sách cá. |
|
muaRauThanhCong(veggieList) |
"Mang rau về, xanh mướt nè!" | Trả về action { type: MUA_RAU_THANH_CONG, payload: veggieList } với danh sách rau. |
|
muaThitThanhCong(meatList) |
"Mang thịt về, tươi rói nè!" | Trả về action { type: MUA_THIT_THANH_CONG, payload: meatList } với danh sách thịt. |
|
hetHang(error) |
"Chợ hết hàng, báo lỗi đây!" | Trả về action { type: HET_HANG, payload: error } với thông báo lỗi. |
|
Thunk Actions | muaCa() |
"Người đi chợ mua cá." | Gọi API, dispatch chayRaCho , rồi dispatch muaCaThanhCong (nếu thành công) hoặc hetHang (nếu lỗi). |
muaRau() |
"Người đi chợ mua rau." | Gọi API, dispatch chayRaCho , rồi dispatch muaRauThanhCong (nếu thành công) hoặc hetHang (nếu lỗi). |
|
muaThit() |
"Người đi chợ mua thịt." | Gọi API, dispatch chayRaCho , rồi dispatch muaThitThanhCong (nếu thành công) hoặc hetHang (nếu lỗi). |
|
Reducer | marketReducer |
"Người sắp xếp tủ lạnh." | Nhận action, cập nhật state (dangChayRaCho , fishList , veggieList , meatList , error ) dựa trên loại action. |
Giải thích chi tiết
1. Action Types
- Giống như tờ giấy ghi chú khi bạn sai người đi chợ. Mỗi ghi chú nói rõ việc cần làm:
CHAY_RA_CHO
: "Đi chợ đây, chờ tui nha!"MUA_CA_THANH_CONG
: "Mua được cá rồi, đây là danh sách cá!"MUA_RAU_THANH_CONG
: "Mua được rau rồi, đây là rau!"MUA_THIT_THANH_CONG
: "Mua được thịt rồi, đây là thịt!"HET_HANG
: "Chợ hết hàng, tui chịu thua!"
- Vai trò: Định nghĩa các loại hành động để reducer biết cách xử lý.
2. Action Creators
- Giống như người viết ghi chú, chuẩn bị thông tin trước khi đưa cho người đi chợ hoặc người sắp xếp tủ lạnh.
chayRaCho()
: Viết ghi chú báo là đang đi chợ.muaCaThanhCong(fishList)
: Viết ghi chú kèm danh sách cá mua được.muaRauThanhCong(veggieList)
: Viết ghi chú kèm danh sách rau mua được.muaThitThanhCong(meatList)
: Viết ghi chú kèm danh sách thịt mua được.hetHang(error)
: Viết ghi chú báo lỗi khi chợ hết hàng.
- Vai trò: Tạo ra các action (object) với
type
vàpayload
(dữ liệu đi kèm) để gửi cho reducer.
3. Thunk Actions
- Giống như người đi chợ thực thụ, họ nhận lệnh (nhấn nút "Mua cá", "Mua rau", "Mua thịt") và:
- Báo: "Đang chạy ra chợ..." (
dispatch(chayRaCho())
). - Tới chợ, kiểm tra hàng bằng cách gọi API (dùng
axios.get
). - Nếu có hàng, mang về danh sách và báo thành công (
dispatch(muaCaThanhCong)
, v.v.). - Nếu hết hàng, báo lỗi (
dispatch(hetHang)
).
- Báo: "Đang chạy ra chợ..." (
- Vai trò: Redux Thunk cho phép viết logic bất đồng bộ (gọi API) trong action, thay vì nhét vào component.
Ví dụ cụ thể (trong muaCa
):
const muaCa = () => {
return (dispatch) => {
dispatch(chayRaCho()); // Báo: Đang đi chợ
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((response) => {
const fishList = response.data.map((user, index) => ({
id: user.id,
name: `Cá ${index + 1}: ${user.name}`,
}));
dispatch(muaCaThanhCong(fishList)); // Báo: Mua được cá
})
.catch((error) => {
dispatch(hetHang('Hôm nay chợ hết cá rồi!')); // Báo: Lỗi
});
};
};
4. Reducer
- Giống như người sắp xếp tủ lạnh, nhận ghi chú (action) và:
- Cập nhật trạng thái
dangChayRaCho
(đang đi chợ hay không). - Lưu danh sách cá (
fishList
), rau (veggieList
), thịt (meatList
) vào các ngăn riêng. - Ghi lại lỗi (
error
) nếu có.
- Cập nhật trạng thái
- Vai trò: Cập nhật trạng thái (state) của ứng dụng dựa trên action, giữ mọi thứ ngăn nắp trong store.
Ví dụ cụ thể (trong marketReducer
):
const initialState = {
dangChayRaCho: false,
fishList: [],
veggieList: [],
meatList: [],
error: '',
};
const marketReducer = (state = initialState, action) => {
switch (action.type) {
case 'CHAY_RA_CHO':
return { ...state, dangChayRaCho: true, error: '' };
case 'MUA_CA_THANH_CONG':
return { ...state, dangChayRaCho: false, fishList: action.payload, error: '' };
case 'MUA_RAU_THANH_CONG':
return { ...state, dangChayRaCho: false, veggieList: action.payload, error: '' };
case 'MUA_THIT_THANH_CONG':
return { ...state, dangChayRaCho: false, meatList: action.payload, error: '' };
case 'HET_HANG':
return { ...state, dangChayRaCho: false, error: action.payload };
default:
return state;
}
};
Luồng hoạt động (tóm tắt)
- Người dùng nhấn nút "Mua cá" → Component gọi
dispatch(muaCa())
. - Thunk action
muaCa
:- Dispatch
chayRaCho()
→ Reducer cập nhậtdangChayRaCho: true
. - Gọi API → Nếu thành công, dispatch
muaCaThanhCong(fishList)
→ Reducer cập nhậtfishList
. - Nếu lỗi, dispatch
hetHang(error)
→ Reducer cập nhậterror
.
- Dispatch
- Component lấy state từ store (dùng
useSelector
) và hiển thị: "Đang chạy ra chợ...", danh sách cá, hoặc lỗi.
Tương tự cho muaRau
và muaThit
.

Ý nghĩa thực tế
- Action Types: Như danh sách việc cần làm khi đi chợ, giúp mọi người hiểu rõ nhiệm vụ.
- Action Creators: Chuẩn bị thông tin chính xác trước khi giao việc.
- Thunk Actions: Người đi chợ thông minh, biết gọi API và xử lý mọi tình huống (thành công/lỗi).
- Reducer: Người quản lý tủ lạnh, giữ mọi thứ ngăn nắp, dễ tìm.