import { useState } from 'react'; export interface IControlledOptions { defaultValuePropName?: string; valuePropName?: string; onChangePropName?: string; } /** * 用于判断组件是否是受控状态,通常存在以下 3 种定义 * 1. props.value 被定义,props.hasOwnProperty("value"),无论 value 值是否是 undefined 或 null,都属于受控 * 2. props.onChange 被定义,props.hasOwnProperty("onChange") * 3. props.value 和 props.onChange 同时被定义 * CNUI 采用第一种,同时内部兼容 params.onChange 存在和不存在两种场景 * * 注意:需要透传组件 props 给当前函数的一个参数,避免受控逻辑判断错误,避免传递了值为 undefined value */ export const useControlled = void>( props: unknown, options?: IControlledOptions, ): [Value | undefined, OnChange] => { const { defaultValuePropName = 'defaultValue', valuePropName = 'value', onChangePropName = 'onChange', } = options || {}; const defaultValue = props[defaultValuePropName] as Value; const propsValue = props[valuePropName] as Value; const propsOnChange = props[onChangePropName] as OnChange; const isControlled = Object.prototype.hasOwnProperty.call( props, valuePropName, ); /** * 当非受控的时候使用内部的 innerValue 兜底,当受控的时候,innerValue 自动失去作用 * 这里的默认值之所有没有使用 value || defaultValue,是因为如果用户传了 params.value,受控的时候,最终不会消费 innerValue */ const [innerValue, setInnerValue] = useState(defaultValue); // 对外暴露的 value,用于替换 props.value const value = isControlled ? propsValue : innerValue; const propsHasOnChange = propsOnChange && typeof propsOnChange === 'function'; /** * 对外暴露的 onChange,用于替换 props.onChange * 当用户未传 onChange 的时候,使用空函数兜底,避免外部调用报错 */ let onChange = (() => {}) as OnChange; if (isControlled) { // 1. 处理受控场景 if (propsHasOnChange) { onChange = propsOnChange; } } else if (propsHasOnChange) { // 2. 处理非受控场景 onChange = ((...paramList) => { // @ts-ignore: Ignoring the type check because propsOnChange is expected to be a function. propsOnChange(...paramList); setInnerValue(paramList[0]); }) as OnChange; } else { onChange = setInnerValue as OnChange; } return [value, onChange]; };