React Native中如何使用date-fns生成年月日下拉选择器真实数据
基于date-fns实现出生日期下拉选择的方案
核心实现思路
不需要手动编写闰年、大小月的判断逻辑,直接调用date-fns内置的日期处理方法生成合法的年/月/日数据源,同时处理年月日选择的联动规则,从根源上避免出现无效日期。
步骤1:引入依赖与工具方法
先安装date-fns依赖,之后在组件文件中引入需要用到的方法:
import { useState, useMemo, useRef } from 'react'; import { View, Text, TouchableOpacity, FlatList, SafeAreaView } from 'react-native'; import { getYear, getDaysInMonth } from 'date-fns'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
步骤2:生成静态年、月数据源
年份范围可根据业务需求调整,常规出生日期选择支持从当前年份往前推120年即可,月份固定为1-12月:
// 生成年份列表:当前年份往前120年到当前年 const currentYear = getYear(new Date()); const years = useMemo(() => { return Array.from({ length: 121 }, (_, i) => { const year = currentYear - 120 + i; return { num: String(year), key: String(year) }; }); }, [currentYear]); // 生成月份列表:1-12月,补零对齐显示格式 const months = useMemo(() => { return Array.from({ length: 12 }, (_, i) => { const month = i + 1; return { num: String(month).padStart(2, '0'), key: String(month) }; }); }, []);
步骤3:动态生成对应年月的日期列表
日期列表为动态值,会根据选中的年、月自动计算,自动处理闰年2月、大小月的天数差异:
// 存储选中的年月日 const [selectedDate, setSelectedDate] = useState({ year: '', month: '', day: '' }); // 根据选中的年、月生成合法的日期列表 const days = useMemo(() => { // 未选年/月时默认按当前年月计算天数,也可根据需求返回空列表 const targetYear = selectedDate.year ? Number(selectedDate.year) : currentYear; const targetMonth = selectedDate.month ? Number(selectedDate.month) - 1 : 0; // date-fns月份从0开始计数 const dayCount = getDaysInMonth(new Date(targetYear, targetMonth)); return Array.from({ length: dayCount }, (_, i) => { const day = i + 1; return { num: String(day).padStart(2, '0'), key: String(day) }; }); }, [selectedDate.year, selectedDate.month, currentYear]);
步骤4:修复原有Dropdown组件的问题
原组件写死了模拟的days数据,未使用传入的data属性,同时状态管理不支持父子联动,修正后核心代码如下:
const Dropdown = ({ label, data, value, onChange }) => { const DropdownButton = useRef(); const [visible, setVisible] = useState(false); const toggleFunction = () => { setVisible(prev => !prev); }; const onItemPress = item => { onChange(item); // 选中值回传给父组件 setVisible(false); }; const renderItem = ({ item }) => ( <TouchableOpacity style={styles.item} onPress={() => onItemPress(item)}> <Text style={styles.buttonText}>{item.num}</Text> </TouchableOpacity> ); const renderDropdown = () => { return ( <SafeAreaView style={styles.dropdown}> <FlatList data={data} // 替换为传入的动态数据,不再写死模拟值 renderItem={renderItem} keyExtractor={(item) => item.key} // 用数据自带key,避免索引发问题 /> </SafeAreaView> ); }; return ( <> <View style={{zIndex: 10}} > <TouchableOpacity onPress={toggleFunction} ref={DropdownButton} style={styles.button} > <Text style={styles.buttonText}> {value === '' ? label : value} </Text> <MaterialCommunityIcons name="chevron-down" color={'#58abc8'} size={16} /> {visible ? renderDropdown() : null} </TouchableOpacity> </View> { visible ? <View style={styles.modal}> <TouchableOpacity style ={styles.overlay} onPress={toggleFunction} ></TouchableOpacity> </View>: null} </> ); };
步骤5:页面中引入三个下拉组件实现联动
// 切换年/月时清空已选日期,避免出现2月31日这类无效值 const handleYearChange = (item) => { setSelectedDate(prev => ({...prev, year: item.num, day: ''})); }; const handleMonthChange = (item) => { setSelectedDate(prev => ({...prev, month: item.num, day: ''})); }; const handleDayChange = (item) => { setSelectedDate(prev => ({...prev, day: item.num})); }; // 渲染选择区域 <View style={{flexDirection: 'row', gap: 12}}> <Dropdown label="选择年" data={years} value={selectedDate.year} onChange={handleYearChange} /> <Dropdown label="选择月" data={months} value={selectedDate.month} onChange={handleMonthChange} /> <Dropdown label="选择日" data={days} value={selectedDate.day} onChange={handleDayChange} /> </View>
注意点
- date-fns的月份计数从0开始(0代表1月,11代表12月),生成数据和计算天数时要做好数值偏移
- 切换年/月时必须清空已选的日,避免出现非法日期
- 如果需要限制最大/最小可选日期(比如禁止选择未来日期),直接在生成年/月/日数组时加过滤条件即可,不需要改动核心逻辑
内容的提问来源于stack exchange,提问作者wafa.A
相关产品推荐
相关产品推荐

