基于验证状态的DatePicker动态边框颜色实现方案
实现带动态边框颜色的受控日期输入框(react-datepicker + react-input-mask)
需求规则
- 默认状态:边框使用初始颜色
#9095A1,无阴影 - 聚焦状态:输入框为空时,边框变为紫色,添加紫色阴影
- 无效状态:输入内容不完整、格式错误或未填写时,边框变为红色,添加红色阴影
- 有效状态:输入内容为合法日期时,边框变为绿色,添加绿色阴影
优化实现代码
import { useState } from 'react'; import { Controller } from 'react-hook-form'; import DatePicker from 'react-datepicker'; import InputMask from 'react-input-mask'; import { parse, format, isValid } from 'date-fns'; const ControlledDateInput = ({ control, name, disabled = false, placeholder = 'MM/DD/YYYY', minDate, maxDate, IconLeft, IconRight }) => { const [isFocused, setIsFocused] = useState(false); return ( <Controller control={control} name={name} rules={{ validate: (value) => { if (!value) return 'Date is required'; const cleanedValue = value.replace(/_/g, '').trim(); if (cleanedValue.length < 10) return 'Incomplete date'; const parsedDate = parse(cleanedValue, 'MM/dd/yyyy', new Date()); if (!isValid(parsedDate)) return 'Invalid date format'; return true; }, }} render={({ field, fieldState }) => { // 判断是否无效:验证错误、内容不完整、包含占位符下划线 const isInvalid = fieldState.error || field.value.includes('_') || field.value.length < 10; // 计算边框颜色和阴影 let borderColor = '#9095A1'; let boxShadow = 'none'; if (isFocused && !field.value) { // 聚焦且为空的状态 borderColor = 'purple'; boxShadow = '0 0 5px purple'; } else if (isInvalid) { // 无效状态 borderColor = 'red'; boxShadow = '0 0 5px rgb(201,23,23)'; } else if (field.value) { // 有效状态 borderColor = 'green'; boxShadow = '0 0 5px rgb(40,224,24)'; } return ( <span className={`input ${isInvalid ? 'error' : field.value ? 'valid' : ''}`} style={{ borderColor, boxShadow }} > {IconLeft && <span className="icon">{IconLeft}</span>} <DatePicker disabled={disabled} shouldCloseOnSelect={true} placeholderText={placeholder} selected={ field.value.length === 10 ? parse(field.value, 'MM/dd/yyyy', new Date()) : null } minDate={minDate} maxDate={maxDate} onChange={(date) => { const formattedDate = date ? format(date, 'MM/dd/yyyy') : ''; field.onChange(formattedDate); }} customInput={ <InputMask mask="99/99/9999" value={field.value || ''} onChange={(e) => field.onChange(e.target.value)} onFocus={() => setIsFocused(true)} onBlur={() => { setIsFocused(false); // 清空不完整的日期 if (field.value.includes('_') || field.value.length < 10) { field.onChange(''); } }} disabled={disabled} /> } /> {IconRight && <span className="icon">{IconRight}</span>} </span> ); }} /> ); }; export default ControlledDateInput;
关键优化说明
- 状态驱动聚焦样式:用
useState维护isFocused状态,替代直接操作DOM样式,符合React状态驱动的设计理念 - 集中样式计算:把边框颜色和阴影的判断逻辑集中处理,避免在多个事件回调中重复编写样式代码,提升代码可维护性
- 精准无效状态判断:结合react-hook-form的验证错误、输入内容长度和占位符下划线,全面覆盖无效场景
- 状态视图同步:在失焦时自动清空不完整日期,确保表单状态和输入框内容一致
内容的提问来源于stack exchange,提问作者David Gyulinyan
相关产品推荐
相关产品推荐

