React预订应用:提交表单后移除已选日期的时段选项
预订应用时段过滤实现方案
核心问题分析
当前方案仅存储单个预订的日期和时段,无法支持多预订场景;同时未在获取可用时段时结合LocalStorage的已预订数据进行过滤,导致已选时段仍会显示。以下是针对性的修改方案:
1. 重构LocalStorage存储结构
将原来的单条存储改为存储预订数组,记录多个日期的已预订时段:
// 父组件formSubmission函数修改 const formSubmission = (values) => { console.log('Form submitted:', values); if (window.submitAPI(values)) { alert('Your reservation has been submitted!'); // 读取现有预订数据,若无则初始化空数组 const existingBookings = JSON.parse(localStorage.getItem('bookings')) || []; // 添加新预订 existingBookings.push({ date: values.date, time: values.time }); // 存回LocalStorage localStorage.setItem('bookings', JSON.stringify(existingBookings)); navigate('/ConfirmedBooking'); } else { alert('There was an error with your submission. Please try again.'); } }
2. 在BookingForm中过滤已预订时段
在获取可用时段后,结合LocalStorage中的预订数据过滤掉已被选择的时段,同时监听数据变化确保选项实时刷新:
// BookingForm组件修改 import { Button } from '../../components/Button' import silhouetteLogo from '../../images/silouhette_logo-removebg-preview.png' import { useFormik } from 'formik'; import * as Yup from 'yup'; import { DateComponent } from '../../components/Date'; import 'bootstrap/dist/css/bootstrap.min.css'; import { useEffect, useState } from 'react'; export function BookingForm({ formSubmission }) { const [bookedSlots, setBookedSlots] = useState([]); // 读取并监听LocalStorage中的预订数据 useEffect(() => { const getBookedSlots = () => { const bookings = JSON.parse(localStorage.getItem('bookings')) || []; setBookedSlots(bookings); }; // 初始化读取 getBookedSlots(); // 监听其他标签页的LocalStorage修改 window.addEventListener('storage', getBookedSlots); return () => window.removeEventListener('storage', getBookedSlots); }, []); const formik = useFormik({ initialValues: { date: new Date().toLocaleDateString(), time: 'Select Time', firstName: '', email: '', confirmButton: false, }, validationSchema: Yup.object().shape({ date: Yup.date() .test( 'date', 'The date you selected has already passed.', function (date) { return new Date(date) >= new Date(); } ) .required('Required'), time: Yup.string() .notOneOf(['Select Time'], 'Please select one of our available time slots.') .required('Please select one of our available time slots.'), firstName: Yup.string().required('Required').trim(), email: Yup.string().email('Please enter a valid email address').required('Required').trim(), confirmButton: Yup.boolean().oneOf([true], 'You must confirm the information above is accurate to submit your booking'), }), onSubmit: (values) => { formSubmission(values); // 提交后重置时间选择 formik.setFieldValue('time', 'Select Time'); } }); // 获取可用时段并过滤已预订选项 const baseAvailability = window.fetchAPI(formik.values.date); const availability = baseAvailability.filter(time => { return !bookedSlots.some(slot => slot.date === formik.values.date && slot.time === time); }); return ( <form data-testid='booking-form' onSubmit={formik.handleSubmit}> <fieldset className='scheduleCheck'> <h1 className="scheduleCheckTitle">Tell us about your Party!</h1> <div id='scheduleChecker' className='scheduleCheckInputs'> <div className="date"> <label htmlFor='date'>Date</label> <input id='date' type='text' className='form-control visually-hidden' value={formik.values.date} onChange={formik.handleChange} onBlur={formik.handleBlur} /> <DateComponent id='visible-date' selected={formik.values.date} className={formik.touched.date && formik.errors.date ? "form-control error date" : "form-control"} onChange={(d) => formik.setFieldValue('date', d)} onChangeRaw={(e) => formik.setFieldTouched('date', e.target.value)} onBlur={formik.handleBlur} ariaHidden required /> </div> <div className="dropdown time"> <label htmlFor='time'>Time</label> <select id='time' name='time' type="button" className={formik.touched.time && formik.errors.time ? 'btn btn-secondary dropdown-toggle error' : "btn btn-secondary dropdown-toggle"} aria-expanded="false" value={formik.values.time} onChange={formik.handleChange} onBlur={formik.handleBlur} required > <option value='Select Time'>Select Time</option> {availability.map((time, index) => <option data-testid='time-option' className="dropdown-item" value={time} key={index}>{time}</option>)} </select> </div> </div > <div> {formik.touched.date && formik.errors.date ? <div data-testid='validation-field' className='schedule-check-error'>{formik.errors.date}</div> : null} {formik.touched.time && formik.errors.time ? <div data-testid='validation-field' className='schedule-check-error'>{formik.errors.time}</div> : null} </div> </fieldset> <div className='infoInput'> <fieldset className='infoInput'> <div className='bookingTitle'> <h1>Complete Your Booking!</h1> <img className='silhouetteLogo' src={silhouetteLogo} alt='little lemon logo' /> </div> <div className='contactInfo'> <div className="row name"> <div className="col firstName"> <label htmlFor="firstName">First Name</label> <input id='firstName' name='firstName' type="text" className={formik.touched.firstName && formik.errors.firstName ? "form-control error" : "form-control name-input"} value={formik.values.firstName} onChange={formik.handleChange} onBlur={formik.handleBlur} placeholder=' ' required /> {formik.touched.firstName && formik.errors.firstName ? <div data-testid='validation-field' className='error-message'>{formik.errors.firstName}</div> : null} </div> </div> <div className="row phemail"> <div className="col email"> <label htmlFor="email" >Email Address</label> <input id='email' name='email' type="email" className={formik.touched.email && formik.errors.email ? "form-control error" : "form-control email"} value={formik.values.email} onChange={formik.handleChange} onBlur={formik.handleBlur} required /> {formik.touched.email && formik.errors.email ? <div data-testid='validation-field' className='error-message'>{formik.errors.email}</div> : null} </div> </div> </div> </fieldset> <fieldset className="align-items-center completeForm"> <div className='row'> <div className="col form-check mb-2 confirm"> <input className={formik.touched.confirmButton && formik.errors.confirmButton ? 'error form-check-input' : 'form-check-input'} type="checkbox" id="confirmButton" onChange={formik.handleChange} checked={formik.values.confirmButton} onBlur={formik.handleBlur} required /> <label className="form-check-label" htmlFor="confirmButton"> Check here to confirm the above information is accurate </label> {formik.touched.confirmButton && formik.errors.confirmButton ? <div data-testid='validation-field' className='error-message'>{formik.errors.confirmButton}</div> : null} </div> <div className="col bookButton"> <Button testId='submit' type='submit' onClick={formik.handleSubmit} textVariant=' Book My Table' /> </div> </div> </fieldset> </div> </form > ) }
3. 关键逻辑说明
- 存储结构优化:用数组存储所有预订,避免覆盖历史记录,支持多日期多时段的预订管理。
- 实时数据同步:通过
useEffect监听LocalStorage,确保组件能获取最新的预订数据,过滤逻辑实时生效。 - 用户体验优化:表单提交后自动重置时间选择框,避免残留旧数据影响下一次操作。
内容的提问来源于stack exchange,提问作者Laura Gorski
相关产品推荐
相关产品推荐

