如何点击不同座位在Modal显示对应标题并实现单独计数递增?
座位预订应用优化方案
问题需求
开发一个可预订不同座位的应用,核心逻辑:
- 点击座位弹出Modal,显示选中的座位名称(如“您已选择Seat 1”)
- 点击确认后跳转至计数页面,对应座位的计数加1
- 避免组件中出现重复代码
界面说明
- 座位选择页:展示5个带有不同图标和名称的座位卡片
- 计数页面:展示每个座位的当前预订数量及取消按钮
现有代码
Seat.jsx
import React, { useEffect, useState } from "react"; import "./zones.css"; import { RxCross2 } from "react-icons/rx"; import { addSeat_1, add_2, addSeat_3, addSeat_4, addSeat_5, } from "../../../features/trolleys/trolleySlice"; import { useDispatch } from "react-redux"; import { Link } from "react-router-dom"; const Seats = () => { const [show, setShow] = useState(false); const [title, setTitle] = useState({ data: [ { value: "Seat 1", id: 1, }, { value: "Seat 2", id: 2, }, { value: "Seat 3", id: 3, }, { value: "Seat 4", id: 4, }, { value: "Seat 5", id: 5, }, ], }); const toggleTitle = () => { setTitle(title.data.map((item) => item.value === item.id && item.value)); }; const dispatch = useDispatch(); useEffect(() => { document.title = "Zones"; }); return ( <div className="seat__container"> <div onClick={() => setShow(true)} className="seat seat__fresh"> <img src="/assets/freshGoods.png" alt="fresh" /> <p className="seat__text">Seat 1</p> </div> <div onClick={() => setShow(true)} className="seat seat__fruits"> <img src="/assets/fruit_veg.png" alt="dry/good" /> <p className="seat__text">Seat 2</p> </div> <div onClick={() => setShow(true)} className="seat seat__deli"> <img src="/assets/deli.png" alt="dry/good" /> <p className="seat__text">Seat 3</p> </div> <div onClick={() => setShow(true)} className="seat seat__frozen"> <img src="/assets/frozen.png" alt="dry/good" /> <p className="seat__frozen">Seat 4</p> </div> <div onClick={() => setShow(true)} className="seat seat__WindSpritz"> <img src="/assets/wine_spritz.png" alt="electronic" /> <p className="seat__text">Seat 5</p> </div> <div className="seat seat__undraw"> <img className="undraw" src="/assets/undraw_gone_shopping_re_2lau 1.png" alt="undraw" /> </div> {show && ( <div className="modal"> <div className="inner__modal"> <RxCross2 onClick={() => setShow(false)} /> <p className="selected__text">You have selected</p> <p onClick={toggleTitle} className="item__name"> {/*a specific name for a seat about should be displayed here*/} </p> {title.data.map((item) =>( <Link onClick={() => { item.value==="seat 1"&&dispatch(addSeat_1()) item.value==="seat 1"&&dispatch(addSeat_2()) item.value==="seat 1"&&dispatch(addSeat_3()) item.value==="seat 1"&&dispatch(addSeat_4()) item.value==="seat 1"&&dispatch(addSeat_5()) }} to="/room" className="confirm__btn" > Confirm </Link> ))} <button onClick={() => setShow(false)} className="cancel__btn"> Cancel </button> </div> </div> )} </div> ); }; export default Seats;
Room.jsx
import "./room.css"; import { GiCancel } from "react-icons/gi"; import { useEffect } from "react"; import { useSelector } from "react-redux"; const Room = () => { const roomQty = useSelector((state) => state.room); useEffect(() => { document.title = "Room"; }); return ( <div className="room__container"> <div className="room__list"> <div className="room__item__box room__item__box__fresh"> <div className="left__items"> <div className="image__box"> <img src="/assets/seat1s.png" alt="img" /> </div> <p className="room__text">Seat1</p> </div> <div className="right__items"> <span className="item__count">{roomQty.seat1Qty}</span> <div className="button__items"> <GiCancel className="cancel" /> <button className="cancel">Cancel</button> </div> </div> </div> <div className="room__item__box room__item__box__fruit-veg"> <div className="left__items"> <div className="image__box"> <img src="/assets/seat2.png" alt="img" /> </div> <p className="room__text">Seat2</p> </div> <div className="right__items"> <span className="item__count">{roomQty.seat2Qty}</span> <div className="button__items"> <GiCancel className="cancel" /> <button className="cancel">Cancel</button> </div> </div> </div> <div className="room__item__box"> <div className="left__items"> <div className="image__box"> <img src="/assets/seat3.png" alt="img" /> </div> <p className="room__text">Seat3</p> </div> <div className="right__items"> <span className="item__count">{roomQty.seat3Qty}</span> <div className="button__items"> <GiCancel className="cancel" /> <button className="cancel">Cancel</button> </div> </div> </div> <div className="room__item__box room__item__box__frozen"> <div className="left__items"> <div className="image__box"> <img src="/assets/seat4.png" alt="img" /> </div> <p className="room__text">Seat4</p> </div> <div className="right__items"> <span className="item__count">{roomQty.seat4Qty}</span> <div className="button__items"> <GiCancel className="cancel" /> <button className="cancel">Cancel</button> </div> </div> </div> <div className="room__item__box room__item__box__wineSpritz"> <div className="left__items"> <div className="image__box"> <img src="/assets/seat5.png" alt="img" /> </div> <p className="room__text">Seat5</p> </div> <div className="right__items"> <span className="item__count">{roomQty.seat5Qty}</span> <div className="button__items"> <GiCancel className="cancel" /> <button className="cancel">Cancel</button> </div> </div> </div> </div> </div> ); }; export default Room;
优化方案
1. 重构Seat组件,跟踪选中座位
- 新增
selectedSeat状态存储当前选中的座位信息 - 用循环渲染所有座位,避免重复的DOM代码
- 点击座位时设置选中的座位并打开Modal
修改后的Seat.jsx核心部分:
const Seats = () => { const [show, setShow] = useState(false); const [selectedSeat, setSelectedSeat] = useState(null); const dispatch = useDispatch(); // 统一管理座位数据 const seatsData = [ { id: 1, name: "Seat 1", className: "seat__fresh", imgSrc: "/assets/freshGoods.png", imgAlt: "fresh" }, { id: 2, name: "Seat 2", className: "seat__fruits", imgSrc: "/assets/fruit_veg.png", imgAlt: "dry/good" }, { id: 3, name: "Seat 3", className: "seat__deli", imgSrc: "/assets/deli.png", imgAlt: "dry/good" }, { id: 4, name: "Seat 4", className: "seat__frozen", imgSrc: "/assets/frozen.png", imgAlt: "dry/good" }, { id: 5, name: "Seat 5", className: "seat__WindSpritz", imgSrc: "/assets/wine_spritz.png", imgAlt: "electronic" }, ]; useEffect(() => { document.title = "Zones"; }, []); const handleSeatClick = (seat) => { setSelectedSeat(seat); setShow(true); }; const handleConfirm = () => { // 根据选中座位分发对应的action const actionMap = { 1: addSeat_1, 2: add_2, 3: addSeat_3, 4: addSeat_4, 5: addSeat_5, }; if (selectedSeat) { dispatch(actionMap[selectedSeat.id]()); } }; return ( <div className="seat__container"> {/* 循环渲染座位 */} {seatsData.map(seat => ( <div key={seat.id} onClick={() => handleSeatClick(seat)} className={`seat ${seat.className}`} > <img src={seat.imgSrc} alt={seat.imgAlt} /> <p className={seat.id === 4 ? "seat__frozen" : "seat__text"}>{seat.name}</p> </div> ))} <div className="seat seat__undraw"> <img className="undraw" src="/assets/undraw_gone_shopping_re_2lau 1.png" alt="undraw" /> </div> {/* Modal部分 */} {show && selectedSeat && ( <div className="modal"> <div className="inner__modal"> <RxCross2 onClick={() => setShow(false)} /> <p className="selected__text">您已选择</p> <p className="item__name">{selectedSeat.name}</p> <Link onClick={handleConfirm} to="/room" className="confirm__btn" > 确认 </Link> <button onClick={() => setShow(false)} className="cancel__btn"> 取消 </button> </div> </div> )} </div> ); };
2. 优化Redux Slice(可选)
将多个独立的add动作合并为一个通用动作,减少代码重复:
// trolleySlice.js import { createSlice } from '@reduxjs/toolkit'; const initialState = { seat1Qty: 0, seat2Qty: 0, seat3Qty: 0, seat4Qty: 0, seat5Qty: 0, }; const trolleySlice = createSlice({ name: 'trolley', initialState, reducers: { addSeat: (state, action) => { const seatKey = `seat${action.payload}Qty`; state[seatKey] += 1; }, }, }); export const { addSeat } = trolleySlice.actions; export default trolleySlice.reducer;
此时Seat组件中的handleConfirm可以简化为:
const handleConfirm = () => { if (selectedSeat) { dispatch(addSeat(selectedSeat.id)); } };
3. 重构Room组件,循环渲染座位计数
用循环替代重复的DOM代码,统一管理座位数据:
const Room = () => { const roomQty = useSelector((state) => state.room); const seatsData = [ { id: 1, name: "Seat1", className: "room__item__box__fresh", imgSrc: "/assets/seat1s.png" }, { id: 2, name: "Seat2", className: "room__item__box__fruit-veg", imgSrc: "/assets/seat2.png" }, { id: 3, name: "Seat3", className: "", imgSrc: "/assets/seat3.png" }, { id: 4, name: "Seat4", className: "room__item__box__frozen", imgSrc: "/assets/seat4.png" }, { id: 5, name: "Seat5", className: "room__item__box__wineSpritz", imgSrc: "/assets/seat5.png" }, ]; useEffect(() => { document.title = "Room"; }, []); return ( <div className="room__container"> <div className="room__list"> {seatsData.map(seat => ( <div key={seat.id} className={`room__item__box ${seat.className}`} > <div className="left__items"> <div className="image__box"> <img src={seat.imgSrc} alt="img" /> </div> <p className="room__text">{seat.name}</p> </div> <div className="right__items"> <span className="item__count">{roomQty[`seat${seat.id}Qty`]}</span> <div className="button__items"> <GiCancel className="cancel" /> <button className="cancel">取消</button> </div> </div> </div> ))} </div> </div> ); };
内容的提问来源于stack exchange,提问作者saygas
相关产品推荐
相关产品推荐

