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

React票务预订应用座位选择故障求助:单按钮触发全选且计数异常

React票务预订应用选座功能问题修复

问题说明

自学React开发静态票务预订应用,使用Bootstrap做样式。选择票数后打开选座模态框,当前存在两个问题:

  • 点击单个座位按钮时,所有按钮同步变色
  • 票数计数仅能变化一次

现有代码

状态与处理函数

const [select,setSelect]=useState(0);
const [seatCount,setSeatCount]=useState(select);
const [toggleSeat,setToggleSeat]=useState(false);

const ticketCountHandler=(e)=>{
    setSelect(e.target.value);
    console.log(e.target.value);
    setSeatCount(e.target.value)
}

const ticketCount=Array.from({ length: 10 }, (_, index) => index + 1);
  
const seatCountHandler=(e)=>{
    e.preventDefault();
    setToggleSeat(!toggleSeat); 

    if(!toggleSeat){
       setSeatCount(prev=>prev-1)
    }else{
      setSeatCount(prev=>prev+1)
    }
}

座位数下拉菜单

{/*count input start */}
<div className="col-4">
  <select id="count" value={select} onChange={ticketCountHandler} className='form-select'>
      <option selected disabled hidden>Select Ticket Count(max 10)</option>
      {ticketCount.map((value) => (
      <option key={value} value={value}>
               {value}
          </option>
       ))}
      
    </select>
</div>
{/*count input end */}

座位选择模态框

<button type="button" class="btn form-control" data-bs-toggle="modal" data-bs-target="#seat-count">
  Select Seats
</button>

<div class="modal fade" id="seat-count" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h1 class="modal-title fs-5" id="exampleModalLabel">Your Ticket Count {seatCount}</h1>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <div className="row">
          <div className="col-1"></div>
          <div className="col-1 m-2"><button className='btn seat-btn disabled bg-danger'>100</button></div>
          <div className="col-1 m-2"><button className={`btn seat-btn ${toggleSeat?"bg-success":""}`} value={101} onClick={seatCountHandler}>101</button></div>
          <div className="col-1 m-2"><button className={`btn seat-btn ${toggleSeat?"bg-success":""}`} value={102} onClick={seatCountHandler}>102</button></div>
          <div className="col-1"></div>
          <div className="col-1 m-2"><button className={`btn seat-btn ${toggleSeat?"bg-success":""}`} value={103} onClick={seatCountHandler}>103</button></div>
          <div className="col-1 m-2"><button className={`btn seat-btn ${toggleSeat?"bg-success":""}`} value={104} onClick={seatCountHandler}>104</button></div>
          <div className="col-1 m-2"><button className={`btn seat-btn ${toggleSeat?"bg-success":""}`} value={105} onClick={seatCountHandler}>105</button></div>
          <div className="col-1"></div>
          <div className="col-1"></div>
        </div>
      </div>
    </div>
  </div>
</div>

问题原因与修复方案

问题1:所有座位同步变色

当前用单个布尔值toggleSeat控制所有座位的选中状态,点击任意座位都会触发全局状态更新,导致所有按钮样式同步变化。需改为记录已选中的座位ID集合,而非单个布尔值。

问题2:票数计数仅能变化一次

seatCountHandler中依赖toggleSeat的当前值增减计数,但setToggleSeat是异步更新,导致逻辑判断出错。同时,计数逻辑应基于单个座位的选中/取消状态,而非全局开关。

修复后的代码

状态调整

// 用数组存储已选中的座位ID
const [selectedSeats, setSelectedSeats] = useState([]);
const [select, setSelect] = useState(0);
// 剩余可选座位数 = 总票数 - 已选座位数
const remainingSeats = select - selectedSeats.length;

座位点击处理函数修改

const seatCountHandler = (e) => {
  e.preventDefault();
  const seatId = e.target.value;
  
  // 判断当前座位是否已选中
  if (selectedSeats.includes(seatId)) {
    // 取消选中:移除对应座位ID
    setSelectedSeats(prev => prev.filter(id => id !== seatId));
  } else {
    // 选中:仅当还有剩余座位时添加
    if (remainingSeats > 0) {
      setSelectedSeats(prev => [...prev, seatId]);
    }
  }
}

模态框座位按钮样式修改

每个座位按钮根据自身ID是否在selectedSeats数组中判断样式,同时添加禁用逻辑:

<div className="col-1 m-2">
  <button 
    className={`btn seat-btn ${selectedSeats.includes("101") ? "bg-success" : ""}`} 
    value="101" 
    onClick={seatCountHandler}
    disabled={remainingSeats === 0 && !selectedSeats.includes("101")}
  >
    101
  </button>
</div>
<div className="col-1 m-2">
  <button 
    className={`btn seat-btn ${selectedSeats.includes("102") ? "bg-success" : ""}`} 
    value="102" 
    onClick={seatCountHandler}
    disabled={remainingSeats === 0 && !selectedSeats.includes("102")}
  >
    102
  </button>
</div>
// 其他座位按钮按相同逻辑修改

模态框标题更新

<h1 class="modal-title fs-5" id="exampleModalLabel">
  已选座位:{selectedSeats.length}/{select}
</h1>

额外优化

  • 移除冗余状态seatCount,直接通过select - selectedSeats.length计算剩余座位数,减少状态维护成本
  • 添加按钮禁用逻辑,避免用户选择超过预订票数的座位

内容的提问来源于stack exchange,提问作者Y Nawodya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 03:05:34