【问题标题】:Creating a react container component in typescript for a MuiPickersUtilsProvider component在打字稿中为 MuiPickersUtilsProvider 组件创建一个反应容器组件
【发布时间】:2021-09-28 03:11:19
【问题描述】:

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

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

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

    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>
  );
}

【问题讨论】:

    标签: reactjs typescript muipickersutilsprovider


    【解决方案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

    【讨论】:

    • 嗨,Jean,谢谢您的提示,我会努力解决的!此外,这并不能解决我的问题,即拥有第二个组件(例如:DateContainer),该组件调用此数据组件并实现状态。你能帮我解决这个问题吗?
    • 另外我刚刚意识到“自定义日期”不起作用,它在两个日期选择器上始终显示相同的日期:(
    • @CatarinaNogueira 是否希望在父容器中有日期值?您应该在自定义逻辑的默认 switch 语句中添加一些逻辑,还应该添加第二个状态来处理组件中的第二个自定义输入。
    • 嗨,Jean,我想要一个拥有所有状态的父组件,并且只是:
      ,这就是我想要做的但我无法正确地做。关于我将尝试的开关以及关于我也会尝试的第二种状态。所以回到帖子的问题是有一个父组件,其状态将与
      一起使用。你知道我该怎么做吗?
    • 嘿@CatarinaNogueira 刚刚更新了答案,我对如何从父母那里做到这一点的想法进行了更新。
    猜你喜欢
    • 2020-06-03
    • 1970-01-01
    • 2020-09-28
    • 2017-09-02
    • 2020-03-22
    • 1970-01-01
    • 2017-11-28
    • 2017-01-22
    • 1970-01-01
    相关资源
    最近更新 更多