会议室预订应用:如何将参会者邮箱输入值存入数组?
会议室预订应用中正确存储参会者邮箱数组的问题
我正在开发一款会议室预订应用,根据输入的参会人数显示对应数量的邮箱输入框。尝试将输入框的值存入数组时,使用push方法导致每个输入的字符都被单独存入数组,希望能正确将参会者邮箱信息存入数组以便后续使用。
原代码
import React, { useState } from "react"; import { useLocation } from "react-router-dom"; import HoverVideoPlayer from 'react-hover-video-player'; import { Card,Button } from "react-bootstrap"; import '../styles/meetingBook.css'; import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import { TimePicker } from '@mui/x-date-pickers'; import TextField from '@mui/material/TextField'; import Swal from "sweetalert2"; import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; import { LocalizationProvider } from '@mui/x-date-pickers'; import DateTimePicker from '@mui/lab/DateTimePicker'; import DesktopDatePicker from '@mui/lab/DesktopDatePicker'; import MobileDatePicker from '@mui/lab/MobileDatePicker'; import axios from "axios"; export default function MeetingBooking(){ const location = useLocation(); //const[hour, setHour] = useState() const [date, setDate] = useState(new Date()); const [fromTimevalue, setFromTimeValue] = React.useState(new Date('2014-08-18T21:11:54')); const [toTimeValue , setToTimeValue] = React.useState(new Date('2014-08-18T21:11:54')); const fromTime = String(fromTimevalue).slice(16,21) const toTime = String(toTimeValue).slice(16,21) const bkDate = String(date).slice(0,15) let details = location.state.details const fields = []; const[attendees, setAttendees] = useState() const [attendeeDetail, setAttendeesDetail] =useState() const [attEmail, setAttEmail]= useState([]) //Booking a meeting room const bookRoom=async()=>{ //checking if the number of attendees arenot less than 2 if(attendees < 2){ await Swal.fire({ position: 'center', icon: 'info', title: 'Members cannot be less than 2', timer: 10000 }) }else{ console.log("Email",attEmail) await Swal.fire({ title: `You are booking ${details.name} on ${bkDate} from ${fromTime} to ${toTime}`, showDenyButton: true, confirmButtonText: 'Yes', denyButtonText: `No`, }).then(async(result) => { if (result.isConfirmed) { await axios.get(`http://localhost:5000/api/bookingConfirmation/${details.name}/${date}/${fromTime}/${toTime}`) .then((res)=>{ console.log(res.status) if(res.status == 200){ Swal.fire({ position: 'center', icon: 'success', title: 'Meeting rooms booked sucessfully. ', }) } }) } else if (result.isDenied) { console.log("No") } }) } } for (let i = 3; i <= attendees; i++) { if(attendees == 2){ fields.push() }else{ fields.push("Enter the attendee's emailId"+i); } } const handleFromTimeChange = (newValue) => { setFromTimeValue(newValue); }; const handleToTimeChange = (newValue) => { setToTimeValue(newValue); }; //Number of attendees const attenseesNumber=(count)=>{ setAttendees(count) } //gathering attendees input const handleAttendeesEmail=(e)=>{ const fieldName = e.target.name setAttendeesDetail(e.target.value) setAttEmail((tm)=>[...tm,fieldName]) } //console.log("Email",attEmail) return( <div> <div id="mainContent"> <HoverVideoPlayer id="meetingRoom" videoSrc={details.videoUrl} pausedOverlay={ <img src={details.imageUrl} alt="" style={{ // Make the image expand to cover the video's dimensions width: '100%', height: '100%', objectFit: 'cover', }} /> } style={{ // Make the image expand to cover the video's dimensions width: '50%', height: '30%', }}/> <div id="roomDetails" style={{display:'block'}}> <h1><center>{details.name}</center></h1> <h6><center>{details.description}</center></h6> <div> <p><DatePicker selected={date} onChange={date => setDate(date)} id="dtpicker"/></p> <div id="timePicker"> <p>from: </p> <LocalizationProvider dateAdapter={AdapterDateFns} style={{width:'20%', margin:'0%'}}> <TimePicker label="Time" value={fromTimevalue} onChange={handleFromTimeChange} renderInput={(params) => <TextField {...params} />} /> </LocalizationProvider> <p>To: </p> <LocalizationProvider dateAdapter={AdapterDateFns}> <TimePicker label="Time" value={toTimeValue} onChange={handleToTimeChange} renderInput={(params) => <TextField {...params} />} /> </LocalizationProvider> </div> <div id="attendeesInfo" > <div style={{display: 'flex',marginLeft:'30%'}}> <center><label>Attendees :</label><input type="number" onChange={(e)=>attenseesNumber(e.target.value)} style={{width:'20%'}}></input></center> </div> <div id="fixFiels" style={{margin:'2%' , display:'block'}}> <div className="inpformating"> <label>EmailId:</label><input type="email" className="formating" onChange={handleAttendeesEmail}></input> </div> <div className="inpformating"> <label>EmailId:</label><input type="email"className="formating" onChange={handleAttendeesEmail}></input> </div> </div> {fields.map(()=>( <div id="infoFields" style={{margin:'2% 5%' , display:'flex'}}> <label>EmailId:</label><input type="email" className="formating" onChange={handleAttendeesEmail}></input> </div> ))} </div> </div> <Button onClick={bookRoom} style={{marginTop: '25%'}}>Book Room</Button> </div> </div> </div> ) }
问题分析
- 输入框无唯一标识:所有邮箱输入框未设置
name或key,无法区分不同输入框的内容变化。 - 状态更新逻辑错误:
handleAttendeesEmail每次触发都往数组新增元素,而非更新对应输入框的值,导致单个字符也被存入数组。 - 动态输入框生成不合理:通过
for循环生成fields数组,组件重渲染时重复生成,未与状态绑定。
解决方案
修改后的完整代码
import React, { useState, useEffect } from "react"; import { useLocation } from "react-router-dom"; import HoverVideoPlayer from 'react-hover-video-player'; import { Card, Button } from "react-bootstrap"; import '../styles/meetingBook.css'; import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import { TimePicker } from '@mui/x-date-pickers'; import TextField from '@mui/material/TextField'; import Swal from "sweetalert2"; import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; import { LocalizationProvider } from '@mui/x-date-pickers'; import axios from "axios"; export default function MeetingBooking(){ const location = useLocation(); const [date, setDate] = useState(new Date()); const [fromTimevalue, setFromTimeValue] = React.useState(new Date('2014-08-18T21:11:54')); const [toTimeValue , setToTimeValue] = React.useState(new Date('2014-08-18T21:11:54')); const fromTime = String(fromTimevalue).slice(16,21); const toTime = String(toTimeValue).slice(16,21); const bkDate = String(date).slice(0,15); let details = location.state.details; const[attendees, setAttendees] = useState(2); // 默认参会人数为2 const [attEmail, setAttEmail] = useState([]); // 参会人数变化时,初始化对应长度的邮箱数组 useEffect(() => { if (attendees >= 2) { setAttEmail(Array(attendees).fill('')); } }, [attendees]); // 预订会议室逻辑 const bookRoom=async()=>{ if(attendees < 2){ await Swal.fire({ position: 'center', icon: 'info', title: '参会人数不能少于2人', timer: 10000 }); return; } // 过滤空邮箱 const validEmails = attEmail.filter(email => email.trim() !== ''); if (validEmails.length < attendees) { await Swal.fire({ position: 'center', icon: 'warning', title: '请填写所有参会者邮箱', timer: 10000 }); return; } console.log("参会邮箱", validEmails); await Swal.fire({ title: `你正在预订${details.name},时间:${bkDate} ${fromTime}-${toTime}`, showDenyButton: true, confirmButtonText: '确认', denyButtonText: `取消`, }).then(async(result) => { if (result.isConfirmed) { try { const res = await axios.get(`http://localhost:5000/api/bookingConfirmation/${details.name}/${date}/${fromTime}/${toTime}`); if(res.status === 200){ Swal.fire({ position: 'center', icon: 'success', title: '会议室预订成功!', }); } } catch (error) { console.error(error); Swal.fire({ position: 'center', icon: 'error', title: '预订失败,请重试', }); } } else if (result.isDenied) { console.log("取消预订"); } }); } const handleFromTimeChange = (newValue) => { setFromTimeValue(newValue); }; const handleToTimeChange = (newValue) => { setToTimeValue(newValue); }; // 更新参会人数 const attenseesNumber=(e)=>{ const count = parseInt(e.target.value); setAttendees(count >=2 ? count : 2); } // 更新对应索引的邮箱值 const handleAttendeesEmail=(index, e)=>{ const newEmails = [...attEmail]; newEmails[index] = e.target.value; setAttEmail(newEmails); } return( <div> <div id="mainContent"> <HoverVideoPlayer id="meetingRoom" videoSrc={details.videoUrl} pausedOverlay={ <img src={details.imageUrl} alt="会议室预览图" style={{ width: '100%', height: '100%', objectFit: 'cover', }} /> } style={{ width: '50%', height: '30%', }}/> <div id="roomDetails" style={{display:'block'}}> <h1><center>{details.name}</center></h1> <h6><center>{details.description}</center></h6> <div> <p><DatePicker selected={date} onChange={date => setDate(date)} id="dtpicker"/></p> <div id="timePicker"> <p>开始时间: </p> <LocalizationProvider dateAdapter={AdapterDateFns} style={{width:'20%', margin:'0%'}}> <TimePicker label="时间" value={fromTimevalue} onChange={handleFromTimeChange} renderInput={(params) => <TextField {...params} />} /> </LocalizationProvider> <p>结束时间: </p> <LocalizationProvider dateAdapter={AdapterDateFns}> <TimePicker label="时间" value={toTimeValue} onChange={handleToTimeChange} renderInput={(params) => <TextField {...params} />} /> </LocalizationProvider> </div> <div id="attendeesInfo" > <div style={{display: 'flex',marginLeft:'30%'}}> <center><label>参会人数 :</label><input type="number" min="2" value={attendees} onChange={attenseesNumber} style={{width:'20%'}}></input></center> </div> {/* 动态渲染所有邮箱输入框 */} {attEmail.map((email, index) => ( <div key={index} className="inpformating" style={{margin:'2%'}}> <label>邮箱 {index + 1}:</label> <input type="email" className="formating" value={email} onChange={(e) => handleAttendeesEmail(index, e)} /> </div> ))} </div> </div> <Button onClick={bookRoom} style={{marginTop: '25%'}}>预订会议室</Button> </div> </div> </div> ) }
关键修改点
- 状态初始化与联动:默认参会人数设为2,通过
useEffect在参会人数变化时生成对应长度的空邮箱数组。 - 动态输入框渲染:基于
attEmail数组直接渲染输入框,每个输入框绑定唯一索引,确保身份可识别。 - 邮箱更新逻辑:
handleAttendeesEmail接收输入框索引,更新数组对应位置的值,避免重复添加元素。 - 输入验证:预订时过滤空邮箱,确保所有必填信息已填写;限制参会人数最小值为2,避免无效输入。
内容的提问来源于stack exchange,提问作者gauthami vijay
相关产品推荐
相关产品推荐

