Files
alert/src/components/AsyncSwitch/index.tsx
2026-04-24 20:30:52 +08:00

84 lines
2.5 KiB
TypeScript

import { Switch, type SwitchProps } from 'antd';
import React, { useEffect, useState } from 'react';
interface AsyncSwitchProps extends Omit<SwitchProps, 'onChange'> {
// Execute the function asynchronously. If successful, the state will be switched. If failed, the state will remain unchanged.
onAsyncChange: (checked: boolean) => Promise<any>;
// Standard onChange, automatically injected in Form mode
onChange?: (checked: boolean) => void;
// Form.Item binds the value attribute to value by default, and the value attribute of Switch is checked. You can modify the bound value property through valuePropName.
/**
<Form.Item name="fieldA" valuePropName="checked">
<Switch />
</Form.Item>
*/
value?: boolean;
defaultChecked?: boolean;
// Successfully/unsuccessfully hook
onSuccess?: () => void;
onError?: (err: any) => void;
}
const AsyncSwitch: React.FC<AsyncSwitchProps> = (props) => {
const {
value,
defaultChecked,
onChange,
onAsyncChange,
onSuccess,
onError,
disabled,
...rest
} = props;
// 1. Internal maintenance status, the initial value is checked first, followed by defaultChecked
const [innerChecked, setInnerChecked] = useState<boolean>(
!!(value ?? defaultChecked),
);
const [loading, setLoading] = useState<boolean>(false);
// 2. When the external checked changes (such as Form reset or external manual modification), synchronize the internal state
useEffect(() => {
if (value !== void 0) {
setInnerChecked(value);
}
}, [value]);
const handleChange = async (val: boolean) => {
setLoading(true);
try {
// Execute asynchronous logic
await onAsyncChange(val);
// Processing after success:
// If checked is not passed externally (uncontrolled mode), the internal state is updated
if (value === void 0) {
setInnerChecked(val);
}
// Trigger external onChange (if it is Form, Form takes over the status update)
onChange?.(val);
onSuccess?.();
} catch (error) {
console.warn('AsyncSwitch Error:', error);
onError?.(error);
// Failure does nothing and leaves the Switch as is
} finally {
setLoading(false);
}
};
return (
<Switch
{...rest}
loading={loading}
// Always use innerChecked in preference as it is the final representation after synchronizing with the outside
checked={innerChecked}
onChange={handleChange}
disabled={disabled || loading}
/>
);
};
export default AsyncSwitch;