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

React Hook重构:使用部分应用是否属于反模式?

React Hook中广告错误回调的参数传递问题

原始Hook代码

function sendMessage(toId, text) { ... }

const useSendMessage = ({ onSuccess, onError } = {}) => {
  const { showRewardedAd } = useAdMob();
 
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleOnEarnMessagesReward = () => {
    // increase total available messages...
  };

  const handleOnRewardedAdError = (error) => {
     toast.warning(error); // 用toast展示错误

     // 疑问:如何传递toId和text参数?

     // 避免因AdMob服务问题阻塞应用...
     handleOnEarnMessagesReward(); // 模拟发放奖励
     sendMessage();
  };
 
  const sendMessageOrDisplayAd = (toId, text) => { 
    if (!user.totalAvailableMessages) {
      return showRewardedAd({
        onEarnReward: handleOnEarnMessagesReward,
        onError: handleOnRewardedAdError,
      });
    }

    // 无需展示激励广告
    sendMessage(toId, text);
    user.totalAvailableMessages -= 1; 
  }

  return {
    isLoading,
    error,
    sendMessageOrDisplayAd
  }
}

你的解决方案(部分应用写法)

const handleOnRewardedAdError = (toId, text) => (error) => {
   toast.warning(error); // 用toast展示错误

   // 避免因AdMob服务问题阻塞应用...
   handleOnEarnMessagesReward(); // 模拟发放奖励
   sendMessage(toId, text); // 成功拿到参数
};

const sendMessageOrDisplayAd = (toId, text) => { 
  if (!user.totalAvailableMessages) {
    return showRewardedAd({
      onEarnReward: handleOnEarnMessagesReward,
      onError: handleOnRewardedAdError(toId, text), // 传递参数
    });
  }

  // 无需展示激励广告
  sendMessage(toId, text);
  user.totalAvailableMessages -= 1; 
}

回答:你的写法不是反模式,还有这些替代方案

1. 你的方案完全合理,不属于反模式

这种**部分应用(Partial Application)**是函数式编程里的常规技巧,通过返回一个捕获了toId和text的闭包函数,既保持了回调的独立性,又能正确传递所需参数,代码清晰且易维护,完全没问题。

2. 其他可行的解决方案

方案一:直接在调用处使用匿名回调

如果错误处理逻辑不复杂,可以省去单独定义handleOnRewardedAdError的步骤,直接在showRewardedAd的配置里写匿名函数包裹参数:

const sendMessageOrDisplayAd = (toId, text) => { 
  if (!user.totalAvailableMessages) {
    return showRewardedAd({
      onEarnReward: handleOnEarnMessagesReward,
      onError: (error) => {
        toast.warning(error);
        handleOnEarnMessagesReward();
        sendMessage(toId, text);
      },
    });
  }

  sendMessage(toId, text);
  user.totalAvailableMessages -= 1; 
}

优点是直观,不需要额外的函数定义;缺点是如果错误处理逻辑复杂,会让sendMessageOrDisplayAd变得臃肿。

方案二:使用useRef保存参数

如果需要在多个回调中共享这两个参数,或者担心闭包捕获旧值的问题,可以用useRef来存储:

const useSendMessage = ({ onSuccess, onError } = {}) => {
  const { showRewardedAd } = useAdMob();
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const messageParamsRef = useRef({ toId: '', text: '' }); // 定义ref

  const handleOnEarnMessagesReward = () => {
    // increase total available messages...
  };

  const handleOnRewardedAdError = (error) => {
     toast.warning(error);
     handleOnEarnMessagesReward();
     sendMessage(messageParamsRef.current.toId, messageParamsRef.current.text); // 从ref取参数
  };
 
  const sendMessageOrDisplayAd = (toId, text) => { 
    messageParamsRef.current = { toId, text }; // 更新ref
    if (!user.totalAvailableMessages) {
      return showRewardedAd({
        onEarnReward: handleOnEarnMessagesReward,
        onError: handleOnRewardedAdError,
      });
    }

    sendMessage(toId, text);
    user.totalAvailableMessages -= 1; 
  }

  return { isLoading, error, sendMessageOrDisplayAd }
}

这种方式适合参数需要跨回调共享的场景,但相比部分应用,多了ref的维护成本,一般场景下没必要用。

方案三:传递包含所有参数的对象

把错误和业务参数包装成一个对象传递,让回调函数的参数更明确:

const handleOnRewardedAdError = ({ error, toId, text }) => {
   toast.warning(error);
   handleOnEarnMessagesReward();
   sendMessage(toId, text);
};

const sendMessageOrDisplayAd = (toId, text) => { 
  if (!user.totalAvailableMessages) {
    return showRewardedAd({
      onEarnReward: handleOnEarnMessagesReward,
      onError: (error) => handleOnRewardedAdError({ error, toId, text }),
    });
  }

  sendMessage(toId, text);
  user.totalAvailableMessages -= 1; 
}

优点是回调函数的参数结构清晰,可读性强;缺点是每次调用需要手动组装参数对象。

总结

你的部分应用写法是完全合规的实践,不是反模式。选择哪种方案取决于你的代码复杂度和维护需求:

  • 逻辑简单:匿名回调最直观;
  • 逻辑复杂:部分应用或参数对象的方式更利于代码拆分;
  • 需要共享参数:useRef方案更合适。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 18:45:31