useRouteLeaveConfirm 路由离开确认弹窗 Hook

组件说明:

useRouteLeaveConfirm 是一个自定义 React Hook,用于在页面有未保存内容时,阻止路由切换并弹出确认弹窗。支持自定义弹窗内容和按钮,适用于表单编辑等场景。

代码实现:

## 代码实现

import { useEffect, useRef } from 'react';
import { useBlocker } from 'react-router-dom';
import { Modal, Button } from 'antd';

export function useRouteLeaveConfirm({
  when,
  message = '当前页面有未保存的内容,确定要离开吗?',
  title = '离开确认',
  onOk,
  onCancel,
  confirmConfig = {},
}) {
  const modalRef = useRef(null);
  const blocker = useBlocker(() => when);

  useEffect(() => {
    if (blocker && blocker.state === 'blocked') {
      if (modalRef.current) {
        modalRef.current.destroy();
        modalRef.current = null;
      }

      const handleClose = () => {
        if (modalRef.current) {
          modalRef.current.destroy();
          modalRef.current = null;
        }
      };

      // 从 confirmConfig 中解构出 buttons,其余的 props 依然传递给 Modal
      const { buttons, ...restConfig } = confirmConfig;

      const modalProps = {
        title,
        content: message,
        keyboard: false,
        ...restConfig,
      };

      // 如果传入了自定义 buttons,则使用 footer 来自定义按钮
      if (buttons && buttons.length > 0) {
        modalProps.footer = (
          <div style={{ textAlign: 'right' }}>
            {buttons.map((button, index) => (
              <Button
                className='ml-2'
                key={index}
                {...button.props}
                onClick={async () => {
                  let shouldClose = true;
                  if (button.onClick) {
                    // 允许 onClick 返回 false 来阻止弹窗关闭
                    const result = await Promise.resolve(button.onClick());
                    if (result === false) {
                      shouldClose = false;
                    }
                  }

                  if (button.action === 'proceed') {
                    blocker.proceed?.();
                  } else if (button.action === 'reset') {
                    blocker.reset?.();
                  }

                  if (shouldClose) {
                    handleClose();
                  }
                }}
              >
                {button.text}
              </Button>
            ))}
          </div>
        );
      } else {
        // 否则,使用默认的 onOk 和 onCancel
        modalProps.okText = confirmConfig.okText || '确定';
        modalProps.cancelText = confirmConfig.cancelText || '取消';
        modalProps.onOk = async () => {
          try {
            await onOk?.();
          } finally {
            blocker.proceed?.();
            handleClose();
          }
        };
        modalProps.onCancel = () => {
          onCancel?.();
          blocker.reset?.();
          handleClose();
        };
      }

      modalRef.current = Modal.confirm(modalProps);
    }

    if (!when && modalRef.current) {
      modalRef.current.destroy();
      modalRef.current = null;
    }
  }, [blocker, when, title, message, onOk, onCancel, confirmConfig]);

  useEffect(() => {
    return () => {
      if (modalRef.current) {
        modalRef.current.destroy();
      }
    };
  }, []);
}
```

## API

| 参数         | 类型      | 说明                                   |
| ------------ | --------- | -------------------------------------- |
| when         | boolean   | 是否阻止路由切换并弹窗                 |
| title        | string    | 弹窗标题                               |
| message      | string    | 弹窗内容                               |
| onOk         | function  | 点击“确定”时回调                       |
| onCancel     | function  | 点击“取消”时回调                       |
| confirmConfig| object    | 弹窗配置,支持自定义按钮、内容等        |

### confirmConfig.buttons
- `text`:按钮文本
- `action`:'proceed'(继续切换)、'reset'(取消切换)
- `props`:Antd Button 额外属性
- `onClick`:按钮点击回调,返回 `false` 可阻止弹窗关闭

用法示例:

###使用示例

```jsx
import { useRouteLeaveConfirm } from '@/hooks/useRouteLeaveConfirm';

const confirmConfig = useMemo(() => ({
  buttons: [
    { text: '取消', action: 'reset' },
    { text: '不保存', action: 'proceed' },
    { text: '保存', props: { type: 'primary' },action: 'proceed', onClick: handleSave },
  ],
}), [handleSave]);

useRouteLeaveConfirm({
  when: isDirty,
  title: '您有未保存的更改',
  message: '您想在离开前保存吗?',
  confirmConfig,
});
```

isDirty 这个值用于标记当前页面内容是否被修改但尚未保存。 true 弹框弹出 如果前后数据一致,没有发生什么变化则为false
action: 'proceed' //这个是用来控制执行完事件后是否要跳转页面

## 注意事项
- 当我们执行的保存逻辑是异步函数时,记得加上async/awiat
- 依赖 react-router-dom v6 的 `useBlocker`。
- 弹窗会在路由切换被阻止时自动弹出,无需手动调用。
- 支持自定义按钮和回调,满足复杂交互需求。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值