All files / src/form controlled.ts

100% Statements 4/4
100% Branches 3/3
100% Functions 2/2
100% Lines 4/4

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68                                                                                      60x               781x   781x             781x              
import { SyntheticEvent } from "react";
import { Omit } from "../_type";
import { useDefault } from "../_util/use-default";
 
/**
 * 表单受控组件中,输入输出的统一规范
 *
 * - `value` 用于输入组件当前值
 * - `onChange` 用于回调组件最新值
 */
export interface ControlledProps<
  V,
  E extends SyntheticEvent = SyntheticEvent,
  C extends ChangeContext<E> = ChangeContext<E>
> {
  /**
   * 未提供 `value` 的情况下,提供了 `defaultValue`,则可以当做是非受控组件使用
   */
  defaultValue?: V;
 
  /**
   * 当前值
   */
  value?: V;
 
  /**
   * 值发生变更时进行回调
   * - `value` 变更的目标值
   * - `context` 此次变更的更多上下文信息,其中 `context.event` 可以获得导致变更的 React 事件
   */
  onChange?(value: V, context: C): void;
}
 
/**
 * 表单 `onChange` 事件中,提供的上下文信息
 */
export interface ChangeContext<E extends SyntheticEvent = SyntheticEvent> {
  /**
   * 触发 `onChange` 事件的事件对象
   */
  event: E;
}
 
const noop = () => {};
 
export function useDefaultValue<T, P extends ControlledProps<T>>(
  props: P,
  defaultDefaultValue?: T
) {
  type ReturnType = Omit<P, "defaultValue">;
 
  const { defaultValue, value, onChange = noop, ...restProps } = props;
 
  const [finalValue, finalOnChange] = useDefault(
    value,
    typeof defaultValue === "undefined" ? defaultDefaultValue : defaultValue,
    onChange
  );
 
  /* eslint-disable @typescript-eslint/no-object-literal-type-assertion */
  return {
    value: finalValue,
    onChange: finalOnChange,
    ...restProps,
  } as ReturnType;
  /* eslint-enable @typescript-eslint/no-object-literal-type-assertion */
}