84 lines
2.5 KiB
TypeScript
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;
|