MUI X DatePicker:onDaySelect未返回指定Luxon DateTime类型问题
问题:MUI X DatePicker多选单日时onDaySelect返回Date而非Luxon DateTime类型
我想用MUI X DatePicker做一个可多选单日(非范围)的组件,传入的日期是Luxon的DateTime类型,但onDaySelect方法返回的却是Date类型(控制台输出已验证),导致isDaySelected方法里调用hasSame时直接报错——因为数组里的Date对象根本没有这个方法。想问这是我漏了配置,还是组件本身的bug?
组件代码
import React, {useCallback, useState} from "react"; import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; import {AdapterDateFns} from "@mui/x-date-pickers/AdapterDateFns"; import {PickersDay, StaticDatePicker} from "@mui/x-date-pickers"; import {Badge, TextField} from "@mui/material"; import {DateTime} from "luxon"; const SelectMultipleDates: React.FC = () => { const [selectedDates, setSelectedDates] = useState<DateTime[]>([]); const handleClickOnDay = useCallback((day: DateTime) => { console.log("is dateTime: " + (day instanceof DateTime)); console.log("is Date: " + (day instanceof Date)); setSelectedDates([...selectedDates, day]); }, [selectedDates]); const isDaySelected = useCallback((providedDay: DateTime) => { return selectedDates.some((value) => value.hasSame(providedDay, "day")); }, [selectedDates]); return ( <LocalizationProvider dateAdapter={AdapterDateFns}> <StaticDatePicker displayStaticWrapperAs="desktop" openTo="day" value={DateTime.now()} onChange={() => console.log("nothing")} renderDay={(day: DateTime) => ( <PickersDay key={day.toString()} day={day} selected={isDaySelected(day)} onDaySelect={(day: DateTime) => handleClickOnDay(day)} outsideCurrentMonth={false}/> )} closeOnSelect={false} renderInput={(params) => <TextField {...params} />} /> </LocalizationProvider> ); }; export default SelectMultipleDates;
控制台输出
is dateTime: false SelectMultipleDates.tsx:13 is Date: true SelectMultipleDates.tsx:14
package.json内容
{ "name": "mui-date-picker-test", "version": "0.1.0", "private": true, "dependencies": { "@date-io/date-fns": "^2.14.0", "@emotion/react": "^11.9.3", "@emotion/styled": "^11.9.3", "@mui/material": "^5.9.2", "@mui/x-date-pickers": "^5.0.0-beta.2", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", "@types/luxon": "^3.0.0", "@types/node": "^16.11.46", "@types/react": "^18.0.15", "@types/react-dom": "^18.0.6", "date-fns": "^2.29.1", "luxon": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", "typescript": "^4.7.4", "web-vitals": "^2.1.4" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": [ "react-app", "react-app/jest" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }
解答
核心原因
这不是组件bug,是你选的适配器不对。你当前用的AdapterDateFns是和date-fns库绑定的适配器,它内部只会用原生Date对象处理日期,所以onDaySelect返回的必然是Date类型,和你导入的Luxon没关系。
两种解决办法
方法1:切换到Luxon适配器(推荐)
直接用适配Luxon的适配器,这样组件内部所有日期都会以Luxon DateTime类型返回,完全匹配你的需求。
首先安装依赖:
npm install @date-io/luxon@2.x @mui/x-date-pickers/AdapterLuxon
然后修改组件代码:
import React, {useCallback, useState} from "react"; import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; // 替换成Luxon专属适配器 import {AdapterLuxon} from "@mui/x-date-pickers/AdapterLuxon"; import {PickersDay, StaticDatePicker} from "@mui/x-date-pickers"; import {TextField} from "@mui/material"; import {DateTime} from "luxon"; const SelectMultipleDates: React.FC = () => { const [selectedDates, setSelectedDates] = useState<DateTime[]>([]); // 优化多选逻辑:点击已选中日期时移除它 const handleClickOnDay = useCallback((day: DateTime) => { console.log("is dateTime: " + (day instanceof DateTime)); console.log("is Date: " + (day instanceof Date)); setSelectedDates(prev => prev.some(d => d.hasSame(day, "day")) ? prev.filter(d => !d.hasSame(day, "day")) : [...prev, day] ); }, []); const isDaySelected = useCallback((providedDay: DateTime) => { return selectedDates.some((value) => value.hasSame(providedDay, "day")); }, [selectedDates]); return ( <LocalizationProvider dateAdapter={AdapterLuxon}> <StaticDatePicker displayStaticWrapperAs="desktop" openTo="day" value={DateTime.now()} onChange={() => console.log("nothing")} renderDay={(day: DateTime) => ( <PickersDay key={day.toString()} day={day} selected={isDaySelected(day)} onDaySelect={(day: DateTime) => handleClickOnDay(day)} outsideCurrentMonth={false}/> )} closeOnSelect={false} renderInput={(params) => <TextField {...params} />} /> </LocalizationProvider> ); }; export default SelectMultipleDates;
方法2:手动转换Date为Luxon DateTime(临时方案)
如果不想切换适配器,可以在回调里把返回的Date对象转换成Luxon DateTime:
// 修改handleClickOnDay const handleClickOnDay = useCallback((day: Date) => { const luxonDay = DateTime.fromJSDate(day); setSelectedDates(prev => prev.some(d => d.hasSame(luxonDay, "day")) ? prev.filter(d => !d.hasSame(luxonDay, "day")) : [...prev, luxonDay] ); }, []); // 同时修改renderDay里的类型转换 renderDay={(day: Date) => { const luxonDay = DateTime.fromJSDate(day); return ( <PickersDay key={luxonDay.toString()} day={day} selected={isDaySelected(luxonDay)} onDaySelect={(day: Date) => handleClickOnDay(day)} outsideCurrentMonth={false}/> ); }}
补充说明
MUI X DatePicker的日期类型完全由适配器决定:用AdapterDateFns就用Date,用AdapterLuxon就用Luxon DateTime,用AdapterMoment就用Moment对象。你之前导入了Luxon但没配对应适配器,所以组件根本不会用Luxon处理日期。
内容的提问来源于stack exchange,提问作者emaarco
相关产品推荐
相关产品推荐

