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
相关产品推荐
相关产品推荐

