You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Recoil下Overlay封装优化:简化重复try-finally逻辑的方案问询

基于Recoil封装全局遮罩的withOverlay工具函数方案

核心思路

React Hooks仅允许在组件或自定义Hooks中调用,因此我们可以通过自定义Hook封装withOverlay函数,让它闭包Recoil的状态设置方法,从而在组件中安全调用,同时避免重复代码。

实现步骤

1. 定义Recoil状态Atom

先定义Overlay的状态Atom,可选择基础单请求版或支持多并发请求的进阶版:

import { atom } from 'recoil';

// 基础版:单请求遮罩
export const overlayState = atom({
  key: 'overlayState',
  default: { visible: false, text: '加载中...' },
});

// 进阶版:支持多并发请求(推荐)
// export const overlayState = atom({
//   key: 'overlayState',
//   default: { count: 0, text: '加载中...' },
// });

2. 封装自定义Hook返回withOverlay

在自定义Hook中获取Recoil的状态设置方法,封装withOverlay工具函数:

import { useSetRecoilState } from 'recoil';
import { overlayState } from './path-to-your-atom';

export function useOverlay() {
  const setOverlay = useSetRecoilState(overlayState);

  // 基础版:单请求处理逻辑
  const withOverlay = async (asyncFn, text = '加载中...') => {
    try {
      setOverlay({ visible: true, text });
      return await asyncFn();
    } finally {
      setOverlay({ visible: false, text: '' });
    }
  };

  // 进阶版:多并发请求处理逻辑(替换上面的基础版即可)
  // const withOverlay = async (asyncFn, text = '加载中...') => {
  //   try {
  //     setOverlay(prev => ({
  //       count: prev.count + 1,
  //       // 仅第一个请求设置提示文本,后续请求保留当前文本
  //       text: prev.count === 0 ? text : prev.text
  //     }));
  //     return await asyncFn();
  //   } finally {
  //     setOverlay(prev => ({
  //       count: Math.max(prev.count - 1, 0),
  //       // 最后一个请求结束后清空文本
  //       text: prev.count === 1 ? '' : prev.text
  //     }));
  //   }
  // };

  return { withOverlay };
}

3. 在组件中调用withOverlay

在业务组件中引入自定义Hook,直接用withOverlay包裹异步操作:

import { useOverlay } from './useOverlay';

function SubmitComponent() {
  const { withOverlay } = useOverlay();

  const handleSubmit = async () => {
    try {
      const response = await withOverlay(
        async () => await myApi.post({ data: 'test' }),
        '提交数据中...'
      );
      // 处理请求成功逻辑
      console.log('提交成功:', response);
    } catch (error) {
      // 处理请求失败逻辑
      console.error('提交失败:', error);
    }
  };

  return <button onClick={handleSubmit}>提交</button>;
}

4. 全局遮罩组件适配(进阶版)

如果使用了计数版Atom,全局遮罩组件需根据计数判断是否显示:

import { useRecoilValue } from 'recoil';
import { overlayState } from './path-to-your-atom';

export function GlobalOverlay() {
  const { count, text } = useRecoilValue(overlayState);
  
  if (count === 0) return null;

  return (
    <div style={{
      position: 'fixed',
      top: 0,
      left: 0,
      width: '100%',
      height: '100%',
      backgroundColor: 'rgba(0,0,0,0.5)',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      color: '#fff',
      fontSize: '16px'
    }}>
      {text}
    </div>
  );
}

方案合理性说明

自定义Hook是React官方允许的Hooks使用场景,useOverlay中调用useSetRecoilState完全符合规则;返回的withOverlay函数通过闭包持有状态设置方法,在组件中调用时不会触发Hooks规则报错,同时实现了代码复用。

内容的提问来源于stack exchange,提问作者JaktensTid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 14:30:48