在昨天我們介紹了基本的Redux概念,那今天就要來實現一樣是很常見的收藏功能,當我們按下愛心後,可以在另一個favorite的頁面中看到剛剛加入收藏的景點,那廢話不多說就開始今天的教學吧!
第一步,我們先定義favoriteSlice的name、初始狀態initialState為空陣列,並用reducers接收了兩種動作(actions),第一個是addFavorite,第二個是removeFavorite。
// redux/favoriteSlice.js
import { createSlice } from '@reduxjs/toolkit';
const favoriteSlice = createSlice({
name: 'favorites',
initialState: [],
reducers: {
addFavorite: (state, action) => {
const exists = state.some(favorite => favorite.id === action.payload.id);
if (!exists) {
state.push(action.payload);
}
},
removeFavorite: (state, action) => {
return state.filter(favorite => favorite.id !== action.payload.id);
},
},
});
export const { addFavorite, removeFavorite } = favoriteSlice.actions;
export default favoriteSlice.reducer;
接下來,我們需要創建 Redux store 並將 favoriteSlice 的 reducer 添加到 store 中。
// redux/store.js
import { thunk } from 'redux-thunk';
import { configureStore } from '@reduxjs/toolkit';
import { combineReducers } from 'redux';
import favoritesReducer from './favoriteSlice';
const rootReducer = combineReducers({
favorites: favoritesReducer,
// 其他 reducers
});
export const store = configureStore({
reducer: rootReducer,
devTools: process.env.NODE_ENV !== 'production',
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
}).concat(thunk),
});
export default store;
再來把store匯入到整個專案的最外層,並用Provider包起來,讓裡面的每個檔案都能讀取到Redux所儲存的各種資料與狀態。
import { Provider } from 'react-redux';
import store from "./components/redux/store";
function App() {
return (
<Provider store={store}>
<BrowserRouter>
<NavigationButton />
<Routes>
<Route path="/" element={<Home />} />
<Route path="places/id/:placeId" element={<Place />} />
<Route path="/favorite" element={<Favorite />} />
<Route path="/schedule" element={<Schedule />} />
<Route path="/map" element={<Map />} />
</Routes>
</BrowserRouter>
</Provider>
);
}
為了方便管理,我們會將一些小功能獨立成一個元件,如果以後有其他頁面需要用到的功能的話,就不用再一行一行的寫,維護起來也比較方便。
//components/AddToFavorite/index.jsx
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { HeartFilled, HeartOutlined } from '@ant-design/icons';
import { addFavorite, removeFavorite } from '../redux/favoriteSlice';
import styles from './addToFavorite.module.css';
export default function AddToFavorite({ landmark }) {
const [isFavorite, setIsFavorite] = useState(false);
const favorites = useSelector((state) => state.favorites);
const dispatch = useDispatch();
useEffect(() => {
const isFav = favorites.some(fav => fav.id === landmark.id);
setIsFavorite(isFav);
}, [favorites, landmark.id]);
const handleClick = (e) => {
e.stopPropagation(); // 阻止點擊事件冒泡
if (isFavorite) {
dispatch(removeFavorite(landmark));
console.log('removeFavorite:',landmark.name);
} else {
dispatch(addFavorite({
id: landmark.id,
name: landmark.name,
image: landmark.image,
}));
console.log('addFavorite:',landmark.name)
}
};
return (
<div className={styles.buttonHeart} onClick={handleClick}>
{isFavorite ? <HeartFilled /> : <HeartOutlined />}
</div>
);
}
原本地圖中的Popup中則改成:
import AddToFavorite from "../AddToFavorite";
.
.
.
<Popup className={styles.popup}>
<div className={styles.landmarkName}>
{landmark.name}
</div>
<img
src={landmark.image}
className={styles.img}
/>
<div className={styles.buttonContainer}>
<AddToFavorite landmark={landmark} />
<Button type="primary">加入行程</Button>
</div>
</Popup>
在FavoriteItem中根據參數傳來的內容去顯示,當按下Button時也會觸發removeFavorite
export default function FavoriteItem({place}) {
const dispatch = useDispatch();
const handleRemove =() =>{
dispatch(removeFavorite(place));
}
console.log("placename",place.name);
return (
<>
<div className={styles.favoritebox}>
<div className={styles.placebox}>
<img src={place.image} alt={place.image} width={"150px"} />
<h2 className={styles.name}>{place.name}</h2>
</div>
<div>
{/* <Button onClick={showPromiseConfirm}>With promise</Button> */}
<Button
type="text"
onClick={handleRemove}
icon={<DeleteTwoTone style={{ fontSize: "30px" }} />}
danger
/>
</div>
</div>
</>
);
}
接著在外面的FavoriteList利用useSelector讀取Redux中的內容再傳給FavoriteItem
import styles from "./favorite.module.css";
import { useSelector } from "react-redux";
import { Row, Col } from "antd";
import FavoriteItem from "../FavoriteItem/FavoriteItem";
import Place from "../../pages/Place/Place";
export default function FavoriteList() {
const favorite = useSelector((state) => state.favorites);
return (
<>
<div className={styles.containerbox}>
<h1 className={styles.title}>Your Favorite places</h1>
<Row className={styles.container}>
{favorite.map((place) => (
<Col key={place.id} span={24} className={styles.col}>
<FavoriteItem place={place} />
</Col>
))}
</Row>
</div>
</>
);
}
按下愛心讓他變成實心的時候,可以在收藏頁面中看到該景點。同時也可以再按一次愛心或是垃圾桶的Button來移除此景點。
今天一口氣將收藏功能完整的呈現出來,不知道會不會一下子無法吸收。但Redux真的是很實用的工具,可以將很亂的狀態變數全部集中一起管理,相信各位重複看個幾遍,然後再自己操作後很快就能理解的,大家慢慢吸收吧!