You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何点击不同座位在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 02:06:04