在昨天我們已經可以新增一個行程了,在今天我們要實現把景點加入到該行程的功能,那一開始我們一樣先定義一下redux架構。
新增一個addToSchedule.js的的檔案,那store.js一樣記得做匯入reducer的動作,這邊不多做解釋,想要復習的朋友可以去前幾天的文章復習。我們先使用一個範例的行程確認是否能成功讀取到,再來定義addSchedule與removeSchedule兩個actiion。
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
schedules: [{
scheduleId: '1',
scheduleName: '範例行程',
time: '2024-10-05',
landmarks: [],
},]
};
const addToScheduleSlice = createSlice({
name: 'addToSchedules',
initialState,
reducers: {
addSchedule: (state, action) => {
const {
scheduleName,
scheduleId,
time,
landmarks,
landmark } = action.payload;
let isDuplicate = state.schedules.find(
item => item.scheduleName === scheduleName);
if (!state.schedules) {
console.error("State schedules is undefined");
return;
}
if (!isDuplicate) {
state.schedules.push({
scheduleId,
scheduleName,
time,
landmarks,
});
} else {
console.log('行程名稱重複,無法添加');
const existingSchedule = state.schedules.find(
schedule => schedule.scheduleName === scheduleName);
if (existingSchedule) {
// 檢查是否已存在相同的地標,避免重複添加
const existingLandmark = existingSchedule.landmarks.find(
l => l.name === landmark.name);
if (!existingLandmark) {
// 如果沒有找到相同的 landmark,添加新的 landmark
existingSchedule.landmarks.push(landmark);
}
// 如果地標已存在,則不進行任何操作
} else {
// 如果沒有找到對應的 scheduleName,新增一個新項目
state.schedules.push({
scheduleName: scheduleName,
landmarks: [landmark]
});
}
}
},
removeSchedule: (state, action) => {
const { scheduleName, landmarkName } = action.payload;
const scheduleIndex = state.schedules.findIndex(
schedule => schedule.scheduleName === scheduleName);
if (scheduleIndex !== -1) {
const landmarks = state.schedules[scheduleIndex].landmarks;
const filteredLandmarks = landmarks.filter(
landmark => landmark.name !== landmarkName);
state.schedules[scheduleIndex].landmarks = filteredLandmarks;
}
},
},
});
// export state to global
export const selectScheduleName = (state) => state.addToSchedule.schedules;
export const { addSchedule, removeSchedule } = addToScheduleSlice.actions;
export default addToScheduleSlice.reducer;
在建立行程這裡,我們要更改一下他的架構,除了addScheduleItems之外,我們還要建立一個可以裝入景點資訊的action,其實最後addScheduleItems是用不到的,只是先保留起來確認行程是有成功建立的,架構也比較簡單,如果想刪掉也是可以的。那這邊也用到一個新套件叫uuid的套件,可以隨機給出一個id到我們想要的物件裡,以免出錯。一樣用npm install uuid
安裝。
import { addScheduleItems } from '../redux/scheduleSlice';
import { addSchedule } from '../redux/addToSchedule';
import { v4 as uuidv4 } from 'uuid';
const handleOk = () => {
form.validateFields().then(values => {
dispatch(addSchedule({
scheduleId: uuidv4(),
scheduleName: scheduleName,
time: time,
landmarks
}));
dispatch(addScheduleItems({
scheduleName: scheduleName,
time: time,
}));
};
在這邊利用selectScheduleName讀取已建立的行程,若沒有已建立的行程則會顯示”請先建立行程!”,當我們按下ok按鈕後,會透過dispatch將這個景點加入到此行程中,那加入的動作是用addSchedule來執行,那在這邊也會定義要回傳的資料的架構。而我們之前也用了防止重複加入的功能,所以一個景點只能出現在一個行程中一次。
useDispatch 用來分發 (dispatch) Redux actions。useSelector 從 Redux store 中提取selectScheduleName,並預設為空陣列。如果行程項目為空,會顯示提示訊息 "請先建立行程"。
export default function AddToSchedule({ landmark }) {
const [open, setOpen] = useState(false);
const showModal = () => {
setOpen(true);
console.log('scheduleItem:',scheduleItem)
};
const handleCancel = () => {
setOpen(false);
};
const [value, setValue] = useState(1);
const dispatch = useDispatch();
const scheduleItem = useSelector(selectScheduleName) || [];
const [selectedSchedule, setSelectedSchedule] = useState('');
const onChange = (e) => {
const newSchedule = e.target.value;
setSelectedSchedule(newSchedule);
setValue(e.target.value);
};
const handleOk = () => {
if (scheduleItem.length > 0) {
addToSche();
}
notification.success({
message: '成功新增景點!',
description: `已添加 ${landmark.name} 到${selectedSchedule}中`,
placement: 'top'
});
};
const addToSche = () => {
dispatch(addSchedule({
scheduleName: selectedSchedule,
landmark: {
name: landmark.name,
id: landmark.id,
image: landmark.image,
position: landmark.position,
description: landmark.description,
end_time: landmark.end_time,
qty: 1,
},
}));
};
return (
<div>
<Button type="primary" className={styles.addbox} onClick={showModal}>
加入行程
</Button>
<Modal
title="加入行程"
centered
open={open}
onOk={handleOk}
okType="default"
onCancel={handleCancel}
>
{scheduleItem.length === 0 ? (<div>請先建立行程!</div>) : (
<Radio.Group onChange={onChange} value={value}>
<Space direction="vertical">
{scheduleItem.map((item, index) => (
<Radio key={index} value={item.scheduleName}>
{item.scheduleName}
</Radio>
))}
</Space>
</Radio.Group>)}
</Modal>
</div>
);
}
這邊可以看到我們可以成功將景點加入到已建立的行程。
今天更改了schedule的架構,使他可以裝入景點資訊,而我們也做了防呆機制,避免重複加入同個景點到同個行程中,其他架構都跟加入收藏差不多,有興趣的朋友們可以到<Day 21:使用Redux完成收藏功能>中去複習一下喔。下一篇,我們會將建立的行程放到其他頁面顯示,然後在裡面做更換景點順序的功能,就請各位敬請期待了!