React日期选择器集成Formik后无法显示选中的时间范围
问题描述
我开发了一个可选择起止时间范围的TableDatePicker组件,为了复用性未在内部集成Formik。但在ParkingTypeForm组件中与Formik结合使用时,能正常选择时间,却无法显示已选中的时间范围。
TableDatePicker组件代码
import { useState } from 'react' import DatePicker from 'react-datepicker' import 'react-datepicker/dist/react-datepicker.css' import { StyledDatePickerContent, StyledDatePickerWrapper } from './style' export const TableDatePicker = ({ startName, endName }) => { const [startDate, setStartDate] = useState(null) const [endDate, setEndDate] = useState(null) return ( <StyledDatePickerWrapper> <StyledDatePickerContent> <label className="accordion-label">Start</label> <DatePicker isClearable placeholderText="Select Start Date" name={startName} showTimeSelect dateFormat="MMMM d, yyyy h:mmaa" selected={startDate} selectsStart startDate={startDate} endDate={endDate} maxDate={endDate} onChange={(date) => setStartDate(date)} /> </StyledDatePickerContent> <StyledDatePickerContent> <label className="accordion-label">End</label> <DatePicker isClearable placeholderText="Select End Date" name={endName} showTimeSelect dateFormat="MMMM d, yyyy h:mmaa" selected={endDate} selectsEnd startDate={startDate} endDate={endDate} minDate={startDate} onChange={(date) => setEndDate(date)} filterTime={(d) => { return startDate < d }} /> </StyledDatePickerContent> </StyledDatePickerWrapper> ) }
ParkingTypeForm组件代码
import React, { useImperativeHandle, useState } from 'react'; import { useFormik } from 'formik'; import * as Yup from 'yup'; import { TableDatePicker } from '../../../../components/DatePickerField/DatePickerField'; const ParkingTypeForm = ({ onChange, refId }) => { const [show, setShow] = useState(false); const formik = useFormik({ initialValues: { reservation_start_datetime: '', reservation_end_datetime: '', }, validationSchema: Yup.object({ reservation_start_datetime: Yup.string().required( 'This field is required' ), reservation_end_datetime: Yup.string().required('This field is required'), }), onSubmit: (e) => { e.preventDefault(); watchForm(); console.log('formikValues', formik.values); setShow(true); }, }); useImperativeHandle(refId, () => ({ Submit: async () => { await formik.submitForm(); }, })); function watchForm() { if (onChange) { onChange({ values: formik.values, validated: formik.isSubmitting ? Object.keys(formik.errors).length === 0 : false, }); } } const handleParkingEdit = () => { setShow(false); document?.getElementById('panelsStayOpen-headingTwo')?.children[0]?.click(); }; return ( <div className={`${show ? 'remove' : 'active'} accordion-item show`}> <h2 className="accordion-header" id="panelsStayOpen-headingTwo"> <button className="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#panelsStayOpen-collapseTwo" aria-expanded="false" aria-controls="panelsStayOpen-collapseTwo" > <span className="number">1</span> Parking Type </button> </h2> <div id="panelsStayOpen-collapseTwo" className="accordion-collapse collapse show" aria-labelledby="panelsStayOpen-headingTwo" > <div className="accordion-body"> <form ref={refId}> <div className={'form'}> <div> <label>Parking Type</label> <TableDatePicker startName="reservation_start_datetime" endName="reservation_end_datetime" value={` ${formik.values.reservation_start_datetime}, ${formik.values.reservation_end_datetime} `} onBlur={formik.handleBlur} onChange={formik.handleChange} /> {formik.errors.startName && ( <div style={{ color: 'red' }}> <small>{formik.errors.startName}</small> </div> )} {formik.errors.endName && ( <div style={{ color: 'red' }}> <small>{formik.errors.endName}</small> </div> )} </div> <pre>{JSON.stringify(formik.values, null, 2)}</pre> </div> <div className="btn-group"> <button type="submit" className="submitBtn"> Save & Continue </button> </div> </form> </div> </div> </div> ); }; export default ParkingTypeForm;
问题原因及解决方案
1. 组件状态与Formik状态完全隔离
TableDatePicker用自身的startDate/endDate管理选中值,但Formik的状态存在formik.values中,两者没有任何同步机制:
- 选择时间时仅更新组件内部状态,未同步到Formik;
- Formik的初始值或后续更新值也无法同步到组件内部,导致无法显示已选时间。
2. 传递的props未被组件使用
你向TableDatePicker传递了value、onChange、onBlur,但组件未接收和处理这些props,等于无效传递,无法实现双向绑定。
3. 校验错误字段名不匹配
你用formik.errors.startName获取错误信息,但Formik的校验字段是reservation_start_datetime和reservation_end_datetime,导致错误提示无法正常显示。
修改后的代码
TableDatePicker组件
import { useState, useEffect } from 'react' import DatePicker from 'react-datepicker' import 'react-datepicker/dist/react-datepicker.css' import { StyledDatePickerContent, StyledDatePickerWrapper } from './style' export const TableDatePicker = ({ startName, endName, values, onChange, onBlur }) => { // 从外部values初始化状态,并监听外部变化同步更新 const [startDate, setStartDate] = useState(values[startName] || null) const [endDate, setEndDate] = useState(values[endName] || null) useEffect(() => { setStartDate(values[startName] || null) setEndDate(values[endName] || null) }, [values, startName, endName]) // 内部状态变化时,同步触发外部onChange更新Formik状态 const handleStartChange = (date) => { setStartDate(date) onChange({ target: { name: startName, value: date } }) } const handleEndChange = (date) => { setEndDate(date) onChange({ target: { name: endName, value: date } }) } return ( <StyledDatePickerWrapper> <StyledDatePickerContent> <label className="accordion-label">Start</label> <DatePicker isClearable placeholderText="Select Start Date" name={startName} showTimeSelect dateFormat="MMMM d, yyyy h:mmaa" selected={startDate} selectsStart startDate={startDate} endDate={endDate} maxDate={endDate} onChange={handleStartChange} onBlur={() => onBlur(startName)} /> </StyledDatePickerContent> <StyledDatePickerContent> <label className="accordion-label">End</label> <DatePicker isClearable placeholderText="Select End Date" name={endName} showTimeSelect dateFormat="MMMM d, yyyy h:mmaa" selected={endDate} selectsEnd startDate={startDate} endDate={endDate} minDate={startDate} onChange={handleEndChange} onBlur={() => onBlur(endName)} filterTime={(d) => startDate < d} /> </StyledDatePickerContent> </StyledDatePickerWrapper> ) }
ParkingTypeForm组件调整点
- 修正初始值类型:日期字段初始值设为
null而非空字符串,避免类型不匹配
initialValues: { reservation_start_datetime: null, reservation_end_datetime: null, },
- 修正校验规则:改用
Yup.date()校验日期类型
validationSchema: Yup.object({ reservation_start_datetime: Yup.date().required('This field is required'), reservation_end_datetime: Yup.date().required('This field is required'), }),
- 修正组件传参与错误提示
<div> <label>Parking Type</label> <TableDatePicker startName="reservation_start_datetime" endName="reservation_end_datetime" values={formik.values} onChange={formik.handleChange} onBlur={formik.handleBlur} /> {formik.errors.reservation_start_datetime && ( <div style={{ color: 'red' }}> <small>{formik.errors.reservation_start_datetime}</small> </div> )} {formik.errors.reservation_end_datetime && ( <div style={{ color: 'red' }}> <small>{formik.errors.reservation_end_datetime}</small> </div> )} </div>
内容的提问来源于stack exchange,提问作者simper
相关产品推荐
相关产品推荐

