这篇文章用项目里的真实代码,讲清"高阶函数"的实战用法。读完你将彻底明白:
(1) 为什么要"函数返回函数",
(2) options
、values
到底是什么,
(3) 如何优雅复用到多个日期选择器。
备注 :讲解使用NutUI React 小程序端 DatePicker 组件
实际代码
- 高阶函数(返回真正给组件用的回调):
js
const handleConfirm = (setDesc, setValue) => (options, values) => {
// 添加空值检查
if (!options || !values) {
console.warn('DatePicker onConfirm: options or values is null/undefined');
return;
}
try {
const dateStr = options.map(option => option.label).join('');
setDesc(dateStr);
if (setValue) {
const valueStr = values.join('-');
setValue(valueStr);
}
} catch (error) {
console.error('DatePicker handleConfirm error:', error);
}
};
- 绑定到 DatePicker(这里是"配置后"再交给组件):
jsx
<DatePicker
title='注册日期'
visible={showRegisterDate}
pickerProps={{
popupProps: { zIndex: 1220 }
}}
defaultValue={new Date()}
showChinese
onClose={() => setShowRegisterDate(false)}
onConfirm={handleConfirm(setRegisterDateDesc, value => {
setFormData(prev => ({
...prev,
registerDate: value
}));
})}
/>
这行"看不懂"的语法,其实很简单
把这一行拆开理解:
javascript
const handleConfirm = (setDesc, setValue) => (options, values) => { ... }
等价于:
javascript
function handleConfirm(setDesc, setValue) {
return function onConfirm(options, values) {
// 真正处理 DatePicker 回传的数据
};
}
- 第一次调用(配置阶段):
handleConfirm(setDesc, setValue)
- 告诉它:选完后"如何更新展示文案"(
setDesc
)和"如何写入表单值"(setValue
)
- 告诉它:选完后"如何更新展示文案"(
- 第二次调用(执行阶段):
(options, values) => { ... }
- 由 DatePicker 在"用户点击确定"时,传入它的回调数据
一句话心智模型:handleConfirm(A, B)(X, Y)
- A/B:你提前注入的"处理策略"(如何展示、如何存储)
- X/Y:组件在 onConfirm 时给你的"数据"(选中的 options/values)
(options, values) 到底是什么?
NutUI DatePicker
在 onConfirm 时会给你两份数据:
-
options:每一列被选中的对象,适合用于"展示",例如:
js[{ label: '2024年', value: '2024' }, { label: '01月', value: '01' }, { label: '15日', value: '15' }]
代码用它生成中文描述:
jsoptions.map(o => o.label).join('') // → "2024年01月15日"
-
values:每一列的原始值,适合用于"存储",例如:
js['2024', '01', '15']
代码用它生成提交值:
jsvalues.join('-') // → "2024-01-15"
-
为什么要两份?
- options.label 用于 UI 显示(带"年/月/日")
- values 用于后端或表单(标准化"YYYY-MM-DD")
为什么要用高阶函数(而不是直接写 onConfirm)
- 复用:同样的处理流程,只需替换
setDesc
、setValue
,就能用于"注册日期""有效期至"等多个 DatePicker。 - 解耦:逻辑(如何把 options/values 转化)与状态绑定(写到
registerDate
还是validDate
)分离,改字段不改逻辑。 - 配置化:把"差异点"放到入参,整体更像"回调工厂"。
若不用高阶函数,等价"直写版"是这样(可读但不可复用):
javascript
<DatePicker
onConfirm={(options, values) => {
if (!options || !values) return;
const text = options.map(o => o.label).join('');
setRegisterDateDesc(text);
setFormData(prev => ({ ...prev, registerDate: values.join('-') }));
}}
/>
进阶:把格式化也"配置化"(更通用的工厂)
javascript
const createConfirm =
({ setDesc, setValue, toText = os => os.map(o => o.label).join(''),
toValue = vs => vs.join('-') }) =>
(options, values) => {
if (!options || !values) return;
setDesc(toText(options));
setValue?.(toValue(values));
};
// 用法:注册日期
onConfirm={createConfirm({
setDesc: setRegisterDateDesc,
setValue: v => setFormData(p => ({ ...p, registerDate: v }))
})}
// 用法:有效期至(可用同一套)
onConfirm={createConfirm({
setDesc: setValidDateDesc,
setValue: v => setFormData(p => ({ ...p, validDate: v }))
})}
易错点与建议
- 空值检查要保留:个别情况下(取消或异常)可能出现
options
/values
为空。 - 格式化规范一致:
values.join('-')
保证"YYYY-MM-DD";如果涉及月/日补零,在数据源或格式化器中处理。 - 如果
setValue
内部引用了其他状态,注意闭包与依赖(useCallback
或useRef
)。
结语(最短记忆法)
- 高阶函数 = 先配置、后执行。
- 先告诉它"选完后怎么展示/怎么落库",再让 DatePicker 把"选了什么"交给它。
- options 用于展示(带"年/月/日"),values 用于存储("YYYY-MM-DD")。
handleConfirm
是一个漂亮的"回调工厂",能把重复逻辑一次封装、到处复用。