import React, { useState, useRef, useEffect } from "react";
import { Switch, Modal } from "@tencent/tea-component";

export default function SwitchAsyncExample() {
  return (
    <>
      <p>
        <AsyncSwitch block={false} />
      </p>
      <p>
        <AsyncSwitch block />
      </p>
    </>
  );
}

function AsyncSwitch({ block }) {
  const [checked, loading, change] = useAsyncCheck(block, 1000);
  const blockText = block ? "模拟操作失败" : "模拟操作成功";
  return (
    <Switch
      value={checked}
      loading={loading}
      disabled={loading}
      onChange={value => change(value)}
    >
      {loading ? "请稍候..." : blockText}
    </Switch>
  );
}

// 这里用 React Hooks 实现，使用 class 的同学可以自行改造
/**
 * @param {boolean} block 是否模拟失败情形
 * @param {number} timeout 模拟的异步时长
 * @returns {[boolean, boolean, (checked: boolean) => {}]}
 */
function useAsyncCheck(block, timeout) {
  // 选中和 loading 态
  const [checked, setChecked] = useState(false);
  const [loading, setLoading] = useState(false);

  // 当前执行中的任务
  const taskRef = useRef(null);

  // 组件 unmount 的时候清理当前任务
  useEffect(
    () => () => {
      if (taskRef && taskRef.current) {
        taskRef.current.cancel();
        taskRef.current = null;
      }
    },
    []
  );

  // 处理切换逻辑
  const change = async newChecked => {
    // 先切目标状态，给用户操作反馈
    setChecked(newChecked);
    setLoading(true);

    // 创建切换异步任务
    const task = simulateAsyncTask(block, timeout);
    // 任务挂载起来，可以取消之
    taskRef.current = task;

    // 执行异步任务
    try {
      await task.run();
    } catch (err) {
      // 失败了恢复到原来的勾选状态
      setChecked(checked);
      Modal.error({ message: "切换失败", description: err.message });
    } finally {
      // 无论成功失败，都退出 loading 态，并清理任务引用
      taskRef.current = null;
      setLoading(false);
    }
  };

  return [checked, loading, change];
}

function simulateAsyncTask(block, timeout) {
  let timer;
  return {
    cancel: () => clearTimeout(timer),
    run: () =>
      new Promise((resolve, reject) => {
        timer = setTimeout(
          () => (!block ? resolve() : reject(new Error("blocked!"))),
          timeout
        );
      }),
  };
}
