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

如何让MUI DateTimePicker仅在点击Accept时触发onAccept?

问题:MUI DateTimePicker 仅在点击Accept按钮时触发onAccept

以下是我使用的ResponsiveDateTimePicker组件代码:

import React from 'react'
import { Stack, Typography } from '@mui/material'
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'
import { renderTimeViewClock } from '@mui/x-date-pickers/timeViewRenderers'
import dayjs, { Dayjs } from 'dayjs'

interface ResponsiveDateTimePickerProps {
  value: Dayjs | null
  maxDateTime: Dayjs | null
  minDateTime: Dayjs | null
  disabled?: boolean
  label?: string
  onAccept: (date: Dayjs | null) => void
}

const ResponsiveDateTimePicker: React.FunctionComponent<
  ResponsiveDateTimePickerProps
> = ({ value, maxDateTime, minDateTime, disabled, label, onAccept }) => {
  type textFieldSizes = 'small' | 'medium' | undefined
  type actionBarOptions = 'today' | 'accept'

  const dateTimePickerProps = {
    viewRenderers: !label
      ? {
          hours: renderTimeViewClock,
          minutes: renderTimeViewClock,
          seconds: renderTimeViewClock,
        }
      : undefined,
    value,
    maxDateTime: maxDateTime || undefined,
    minDateTime: minDateTime || undefined,
    onAccept: (date: unknown) => {
      if (dayjs.isDayjs(date)) {
        onAccept(date as Dayjs)
      } else {
        onAccept(null)
      }
    },
    format: 'DD MMMM YYYY, h:mm a',
    disabled,
    slotProps: {
      actionBar: {
        actions: ['today' as actionBarOptions, 'accept' as actionBarOptions],
        sx: { padding: 0 },
      },
      textField: () => ({
        size: 'small' as textFieldSizes,
        InputProps: {
          placeholder: value ? '' : 'Forever',
          sx: {
            height: '40px',
          },
          slotProps: {
            input: {
              sx: {
                height: '21px !important',
                fontSize: '14px !important',
                padding: '8.5px 30px !important',
              },
            },
          },
        },
      }),
    },
  }

  return (
    <Stack direction="row" alignItems="center">
      {label && (
        <Typography
          variant="subtitle1"
          sx={{ minWidth: '120px' }}
          paddingRight={0}
        >
          {label}
        </Typography>
      )}
      <DateTimePicker {...dateTimePickerProps} />
    </Stack>
  )
}

export default ResponsiveDateTimePicker

当前组件的选择流程:

  • 用户点击选择器显示区域
  • 选择日期
  • 选择小时(可切换上午/下午)
  • 选择分钟(可切换上午/下午)

现在遇到的问题:选择完分钟后,组件会自动触发onAccept函数,把这一步当作确认操作。但我需要只有用户主动点击Accept按钮时才调用onAccept,同时允许用户返回修改小时、日期等选项,不打断操作流程。


解决方案

核心思路是:用内部临时状态存储用户的选择过程,关闭自动确认逻辑,仅在Accept按钮点击时同步结果给父组件。

修改后的完整代码:

import React, { useState } from 'react'
import { Stack, Typography } from '@mui/material'
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'
import { renderTimeViewClock } from '@mui/x-date-pickers/timeViewRenderers'
import dayjs, { Dayjs } from 'dayjs'

interface ResponsiveDateTimePickerProps {
  value: Dayjs | null
  maxDateTime: Dayjs | null
  minDateTime: Dayjs | null
  disabled?: boolean
  label?: string
  onAccept: (date: Dayjs | null) => void
}

const ResponsiveDateTimePicker: React.FunctionComponent<ResponsiveDateTimePickerProps> = ({
  value,
  maxDateTime,
  minDateTime,
  disabled,
  label,
  onAccept
}) => {
  // 内部临时状态,存储用户选择过程中的日期值
  const [tempValue, setTempValue] = useState<Dayjs | null>(value)

  // 自定义Accept按钮的点击逻辑
  const handleAcceptClick = () => {
    onAccept(tempValue)
  }

  // 自定义Today按钮的点击逻辑
  const handleTodayClick = () => {
    const today = dayjs()
    setTempValue(today)
    onAccept(today)
  }

  return (
    <Stack direction="row" alignItems="center">
      {label && (
        <Typography variant="subtitle1" sx={{ minWidth: '120px' }} paddingRight={0}>
          {label}
        </Typography>
      )}
      <DateTimePicker
        viewRenderers={!label ? {
          hours: renderTimeViewClock,
          minutes: renderTimeViewClock,
          seconds: renderTimeViewClock,
        } : undefined}
        value={tempValue}
        maxDateTime={maxDateTime || undefined}
        minDateTime={minDateTime || undefined}
        // 关闭选择后自动触发确认的逻辑
        onAccept={() => {}}
        // 选择后不自动关闭弹窗,允许用户返回修改
        closeOnSelect={false}
        // 实时更新内部临时状态,不触发父组件的onAccept
        onChange={(newValue) => setTempValue(newValue)}
        format="DD MMMM YYYY, h:mm a"
        disabled={disabled}
        slotProps={{
          actionBar: {
            // 自定义ActionBar按钮,替换默认的点击逻辑
            actions: [
              {
                label: 'Today',
                onClick: handleTodayClick,
              },
              {
                label: 'Accept',
                onClick: handleAcceptClick,
              },
            ],
            sx: { padding: 0 },
          },
          textField: () => ({
            size: 'small',
            InputProps: {
              placeholder: value ? '' : 'Forever',
              sx: { height: '40px' },
              slotProps: {
                input: {
                  sx: {
                    height: '21px !important',
                    fontSize: '14px !important',
                    padding: '8.5px 30px !important',
                  },
                },
              },
            },
          }),
        }}
      />
    </Stack>
  )
}

export default ResponsiveDateTimePicker

关键修改点说明:

  1. 新增内部临时状态tempValue:用户选择日期、小时、分钟时,只会更新这个临时状态,不会直接触发父组件的onAccept。
  2. 关闭自动确认:设置onAccept={() => {}},覆盖默认的自动触发逻辑。
  3. 禁止选择后自动关闭:添加closeOnSelect={false},用户选完分钟后弹窗不会关闭,可返回修改日期、小时。
  4. 自定义ActionBar按钮:替换默认的actions数组,为Today和Accept按钮绑定自定义点击事件,只有点击Accept时才调用父组件的onAccept。

这样就能实现:用户可以自由修改日期、时间的各个部分,直到主动点击Accept按钮才确认选择,完全符合需求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 23:12:11