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

会议室预订应用:如何将参会者邮箱输入值存入数组?

会议室预订应用中正确存储参会者邮箱数组的问题

我正在开发一款会议室预订应用,根据输入的参会人数显示对应数量的邮箱输入框。尝试将输入框的值存入数组时,使用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>
    )
}

问题分析

  1. 输入框无唯一标识:所有邮箱输入框未设置name或key,无法区分不同输入框的内容变化。
  2. 状态更新逻辑错误:handleAttendeesEmail每次触发都往数组新增元素,而非更新对应输入框的值,导致单个字符也被存入数组。
  3. 动态输入框生成不合理:通过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>
    )
}

关键修改点

  1. 状态初始化与联动:默认参会人数设为2,通过useEffect在参会人数变化时生成对应长度的空邮箱数组。
  2. 动态输入框渲染:基于attEmail数组直接渲染输入框,每个输入框绑定唯一索引,确保身份可识别。
  3. 邮箱更新逻辑:handleAttendeesEmail接收输入框索引,更新数组对应位置的值,避免重复添加元素。
  4. 输入验证:预订时过滤空邮箱,确保所有必填信息已填写;限制参会人数最小值为2,避免无效输入。

内容的提问来源于stack exchange,提问作者gauthami vijay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 22:50:23