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

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组件调整点

  1. 修正初始值类型:日期字段初始值设为null而非空字符串,避免类型不匹配
initialValues: {
  reservation_start_datetime: null,
  reservation_end_datetime: null,
},
  1. 修正校验规则:改用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'),
}),
  1. 修正组件传参与错误提示
<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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 03:15:51