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

如何在TypeScript中为MuiPickersUtilsProvider创建React容器组件

React+TypeScript日期选择组件拆分方案

核心实现思路

  • 定义子组件的Props类型,声明对外暴露的onDateChange回调方法,回调参数为计算得到的起始日期、结束日期
  • 抽离所有日期选择相关的枚举、选项、交互逻辑到子组件内部
  • 修复原代码中自定义时间段两个日期选择器共用同一份状态导致值互相覆盖的问题

子组件完整代码(src/components/DateRangePicker.tsx)

import React, { useState, useEffect } from "react";
import { DatePicker, MuiPickersUtilsProvider } from "@material-ui/pickers";
import ReactSelect, { ValueType } from "react-select";
import DateFnsUtils from "@date-io/date-fns";

export enum DateValueEnum {
  Today = "TODAY",
  Yesterday = "YESTERDAY",
  Custom = "CUSTOM"
}

type OptionType = {
  key: string;
  label: string;
};

const options: OptionType[] = [
  { key: DateValueEnum.Today, label: "今日" },
  { key: DateValueEnum.Yesterday, label: "昨日" },
  { key: DateValueEnum.Custom, label: "自定义" }
];

// 子组件Props类型定义
interface DateRangePickerProps {
  onDateChange: (startDate: Date | undefined, endDate: Date | undefined) => void;
}

export default function DateRangePicker({ onDateChange }: DateRangePickerProps) {
  // 分开存储起始、结束日期,解决原代码值覆盖问题
  const [startDate, setStartDate] = useState<Date | null>(new Date());
  const [endDate, setEndDate] = useState<Date | null>(new Date());
  const [selectedOption, setSelectedOption] = useState<OptionType | null>(options[0]);

  const handleChangeDateType = (value: string) => {
    let computedStart: Date | undefined = undefined;
    let computedEnd: Date | undefined = undefined;
    const startToday = new Date();
    switch (value) {
      case DateValueEnum.Today: {
        computedStart = startToday;
        computedEnd = new Date(computedStart.getTime());
        break;
      }
      case DateValueEnum.Yesterday: {
        computedStart = new Date(startToday.getTime());
        computedStart.setDate(computedStart.getDate() - 1);
        computedEnd = new Date(computedStart.getTime());
        break;
      }
      case DateValueEnum.Custom: {
        computedStart = startDate ?? undefined;
        computedEnd = endDate ?? undefined;
        break;
      }
      default:
    }
    // 调用父组件传入的回调,将值传给父组件
    onDateChange(computedStart, computedEnd);
  };

  const handleChange = (option: ValueType<OptionType>) => {
    if (!option) return;
    const selected = option as OptionType;
    setSelectedOption(selected);
    handleChangeDateType(selected.key);
  };

  // 自定义日期变化时同步通知父组件
  useEffect(() => {
    if (selectedOption?.key === DateValueEnum.Custom) {
      onDateChange(startDate ?? undefined, endDate ?? undefined);
    }
  }, [startDate, endDate, selectedOption, onDateChange]);

  return (
    <div>
      <ReactSelect
        value={selectedOption}
        onChange={handleChange}
        isMulti={false}
        options={options}
      />
      {selectedOption?.key === DateValueEnum.Custom && (
        <div style={{ display: "flex", gap: "5px", marginTop: "8px" }}>
          <div style={{ width: "50%" }}>
            <MuiPickersUtilsProvider utils={DateFnsUtils}>
              <DatePicker
                fullWidth
                margin="normal"
                required
                error={false}
                invalidLabel="Several values..."
                value={startDate}
                onChange={(newDate) => setStartDate(newDate)}
                format="MM/dd/yyyy"
              />
            </MuiPickersUtilsProvider>
          </div>
          <div style={{ width: "50%" }}>
            <MuiPickersUtilsProvider utils={DateFnsUtils}>
              <DatePicker
                fullWidth
                margin="normal"
                required
                error={false}
                invalidLabel="Several values..."
                value={endDate}
                onChange={(newDate) => setEndDate(newDate)}
                format="MM/dd/yyyy"
              />
            </MuiPickersUtilsProvider>
          </div>
        </div>
      )}
    </div>
  );
}

父组件调用示例(src/App.tsx)

import React from "react";
import DateRangePicker from "./components/DateRangePicker";

export default function App() {
  return (
    <div className="App" style={{ padding: "20px", maxWidth: "600px" }}>
      <DateRangePicker
        onDateChange={(start, end) => {
          // 此处直接拿到子组件传递的起止日期,可做后续业务处理
          console.log("父组件拿到的起始日期:", start);
          console.log("父组件拿到的结束日期:", end);
        }}
      />
    </div>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 07:57:04