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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 03:05:55