0

我有一个负责日期时间选择器的组件。我希望另一个父组件调用这个子日期选择器组件,这样我就可以拥有一个“包装器/容器”组件,一个仅用于数据选择器。此外,我在typescript上这样做没有成功。

这是我的代码沙箱:https ://codesandbox.io/s/date-picker-ubosl?file=/src/App.tsx

这是我的代码,该函数handleChangeDateType最后执行一个 console.log() ,结果应该传递给 Parent 组件。

    import React, { useState } 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: "Today" },
  { key: DateValueEnum.Yesterday, label: "Yesterday" },
  { key: DateValueEnum.Custom, label: "Custom" }
];

export default function App() {
  const [selectedDate, setSelectedDate] = React.useState<Date | null>(
    new Date()
  );

  const [selectedOption, setSelectedOption] = useState<ValueType<OptionType>>();

  const handleChangeDateType = (value: string) => {
    let startDate: Date | undefined = undefined;
    let endDate: Date | undefined = undefined;
    const startToday = new Date();
    switch (value) {
      case DateValueEnum.Today: {
        startDate = startToday;
        endDate = new Date(startDate.getTime());
        break;
      }
      case DateValueEnum.Yesterday: {
        startDate = new Date(startToday.getTime());
        startDate.setDate(startDate.getDate() - 1);
        endDate = new Date(startDate.getTime());
        break;
      }
      default: /**  nothing */
    }
    console.log(startDate);
    console.log(endDate);
  };

  const handleChange = (option: ValueType<OptionType>) => {
    setSelectedOption(option.key);
    handleChangeDateType(option.key);
  };

  return (
    <div className="App">
      <div>
        <ReactSelect
          value={selectedOption as ValueType<OptionType>}
          onChange={(option) => handleChange(option)}
          isMulti={false}
          options={options}
        />

        {selectedOption === DateValueEnum.Custom ? (
          <div style={{ display: "flex" }}>
            <div style={{ width: "50%", float: "left", paddingRight: "5px" }}>
              <MuiPickersUtilsProvider utils={DateFnsUtils}>
                <DatePicker
                  fullWidth
                  margin="normal"
                  required={true}
                  error={false}
                  invalidLabel={"Several values..."}
                  value={selectedDate}
                  onChange={(newDate) => setSelectedDate(newDate)}
                  format="MM/dd/yyyy"
                />
              </MuiPickersUtilsProvider>
            </div>
            <MuiPickersUtilsProvider utils={DateFnsUtils}>
              <DatePicker
                fullWidth
                margin="normal"
                required={true}
                error={false}
                invalidLabel={"Several values..."}
                value={selectedDate}
                onChange={(newDate) => setSelectedDate(newDate)}
                format="MM/dd/yyyy"
              />
            </MuiPickersUtilsProvider>
          </div>
        ) : null}
      </div>
    </div>
  );
}
4

1 回答 1

1

编辑:要回答原始问题,我要做的是从 DateComponent 中提取状态并将它们放在父级中,这样您就可以将日期和 onChange 函数从父级传递给日期组件。

// App.tsx
import React from "react";

import { MuiPickersUtilsProvider } from "@material-ui/pickers";

import DateFnsUtils from "@date-io/date-fns";
import DateComponent from "./DateComponent";

export type IDates = {
  startDate: Date | null;
  endDate: Date | null;
};

export type IDatesKeys = keyof IDates;

const formatDate = (date: Date) => date.toLocaleString();

export default function App() {
  const [dates, setDates] = React.useState<IDates>({
    startDate: null,
    endDate: null
  });

  const onChangeDate = (dates: IDates) => {
    setDates(dates);
  };

  // Remember to wrap everything with the MuiPickersUtilsProvider
  return (
    <MuiPickersUtilsProvider utils={DateFnsUtils}>
      <div className="App">
        <DateComponent dates={dates} onChange={onChangeDate} />
        {dates.startDate && <p>Start Date: {formatDate(dates.startDate)}</p>}
        {dates.endDate && <p>End Date: {formatDate(dates.endDate)}</p>}
      </div>
    </MuiPickersUtilsProvider>
  );
}

在您的 DateComponent 之后,您需要执行以下操作:

// DateComponent.tsx
import React from "react";

import { DatePicker } from "@material-ui/pickers";

import ReactSelect, { ValueType } from "react-select";

import { IDates, IDatesKeys } from "./App";

type IDateComponent = {
  dates: IDates;
  onChange: (dates: IDates) => void;
};

export enum DateValueEnum {
  Today = "TODAY",
  Yesterday = "YESTERDAY",
  Custom = "CUSTOM"
}
type OptionType = {
  key: DateValueEnum;
  label: string;
};

const options: OptionType[] = [
  { key: DateValueEnum.Today, label: "Today" },
  { key: DateValueEnum.Yesterday, label: "Yesterday" },
  { key: DateValueEnum.Custom, label: "Custom" }
];

export default function DateComponent({ dates, onChange }: IDateComponent) {
  const [selectedOption, setSelectedOption] = React.useState<
    ValueType<OptionType, false>
  >(null);

  const handleChangeDateType = (value: DateValueEnum) => {
    const today = new Date();
    let startDate = new Date();
    let endDate = new Date();
    switch (value) {
      case DateValueEnum.Today: {
        endDate.setDate(today.getDate() - 1);
        break;
      }
      // No need to do anything here because you will control the custom option with the other datepickers.
      case DateValueEnum.Custom: {
       return;
      }
      // No need for the yesterday case since there're only two cases, the custom will be handled with another function.
      default: {
        startDate.setDate(today.getDate() - 1);
        break;
      }
    }
    // Here you call the function that will update the parent state.
    onChange({ startDate, endDate });
  };

  React.useEffect(() => {
    if (selectedOption) {
      handleChangeDateType(selectedOption.key);
    }
  }, [selectedOption]);

  const handleChange = (option: ValueType<OptionType, false>) => {
    if (option) {
      setSelectedOption(option);
    }
  };

  // This function will update the custom logic, using the object keys to update the right date
  // You need to call it like this customOnChange("startDate") and this will return an onChange valid date function.
  const customOnChange = (key: IDatesKeys) => (date: Date | null) => {
    onChange({
      ...dates,
      [key]: date
    });
  };

  return (
    <div>
      <ReactSelect
        value={selectedOption}
        onChange={handleChange}
        options={options}
        isMulti={false}
        autoFocus
      />

      {selectedOption?.key === DateValueEnum.Custom && (
        <div style={{ display: "flex" }}>
          <div style={{ width: "50%", float: "left", paddingRight: "5px" }}>
            <DatePicker
              fullWidth
              margin="normal"
              required={true}
              error={false}
              invalidLabel={"Several values..."}
              value={dates.startDate}
              onChange={customOnChange("startDate")}
              format="MM/dd/yyyy"
            />
          </div>
          <DatePicker
            fullWidth
            margin="normal"
            required={true}
            error={false}
            invalidLabel={"Several values..."}
            value={dates.endDate}
            onChange={customOnChange("endDate")}
            format="MM/dd/yyyy"
          />
        </div>
      )}
    </div>
  );
}

如果您想看一下,这是一个分叉的沙箱:https ://codesandbox.io/s/date-picker-forked-fyt7t

于 2021-09-28T03:37:39.030 回答