123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- import { cloneElement, isValidElement, ReactNode, useRef } from "react";
- import noop from "@/utils/noop";
- interface Props<Value> {
- value?: Value;
- valueProps?: string;
- onChangeProps?: string;
- waitTime?: number;
- onChange?: (value: Value) => void;
- onFormat?: (...args: any[]) => Value;
- onGuard?: (value: Value, oldValue: Value) => Promise<void>;
- onCatch?: (error: Error) => void;
- children: ReactNode;
- }
- function GuardState<T>(props: Props<T>) {
- const {
- value,
- children,
- valueProps = "value",
- onChangeProps = "onChange",
- waitTime = 0, // debounce wait time default 0
- onGuard = noop,
- onCatch = noop,
- onChange = noop,
- onFormat = (v: T) => v,
- } = props;
- const lockRef = useRef(false);
- const saveRef = useRef(value);
- const lastRef = useRef(0);
- const timeRef = useRef<any>();
- if (!isValidElement(children)) {
- return children as any;
- }
- const childProps = { ...children.props };
- childProps[valueProps] = value;
- childProps[onChangeProps] = async (...args: any[]) => {
- // 多次操作无效
- if (lockRef.current) return;
- lockRef.current = true;
- try {
- const newValue = (onFormat as any)(...args);
- // 先在ui上响应操作
- onChange(newValue);
- const now = Date.now();
- // save the old value
- if (waitTime <= 0 || now - lastRef.current >= waitTime) {
- saveRef.current = value;
- }
- lastRef.current = now;
- if (waitTime <= 0) {
- await onGuard(newValue, value!);
- } else {
- // debounce guard
- clearTimeout(timeRef.current);
- timeRef.current = setTimeout(async () => {
- try {
- await onGuard(newValue, saveRef.current!);
- } catch (err: any) {
- // 状态回退
- onChange(saveRef.current!);
- onCatch(err);
- }
- }, waitTime);
- }
- } catch (err: any) {
- // 状态回退
- onChange(saveRef.current!);
- onCatch(err);
- }
- lockRef.current = false;
- };
- return cloneElement(children, childProps);
- }
- export default GuardState;
|