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

如何等待React Native自定义Hook返回确定值后再执行逻辑?

React Native自定义Hook异步状态等待问题解决

问题概述

我编写了一个自定义React Native Hook useHasSubscription,用于返回用户的订阅状态(true/false)。但在AppLoaded组件中调用时,Hook初始返回null,导致默认渲染SubscriptionScreen,直到内部异步逻辑完成才更新状态,打乱了应用流程。尝试将Hook改为async后出现大量错误,需要解决该问题。

原代码

自定义Hook useHasSubscription

export const useHasSubscription = () => { 
  const [appState, setAppState] = useState(AppState.currentState);
  const [subscribed, setSubscribed] = useState(null)
  const [isLoading, setLoading] = useState(true)

  const {
    connected,
    subscriptions,
    getSubscriptions,
  } = useIAP();

  useEffect(() => {
    
    console.log("Connected? ", connected, "App State: ", appState)
    if (!connected) {
      console.log('not connected')
      return;
    }
    if (appState === "active") {
      console.log('app is active, checking status')
      getSubscriptions({ skus: ["sku"] }).then(()=>{
        return IapIosSk2.subscriptionStatus("sku")
      }).then((res) => {
          console.log("subscription Status: ", res);
          const temp = res.some(({state})=> (
            state === 'subscribed' || state === 'inGracePeriod'
           ));
          setSubscribed(temp);
        })
        .catch((err) => { //test here with error/no internet 
          console.log("failed to get status: ", err);
        });

    }
    setLoading(false)
  }, [
    connected,
    appState,
    getSubscriptions,
    subscribed,
    isLoading]);

    return subscribed
};

AppLoaded 组件

const AppLoaded = () => {

  const isSubscribed = useHasSubscription(); // 等待其返回false或true,而非null

  console.log("is Subbed? " + isSubscribed);

  return (
    <PaperProvider theme={theme}>
      <SafeAreaProvider>
        {isSubscribed ? (
          <NavigationRoot />
        ) : (
          <SubscriptionScreen />
        )}
      </SafeAreaProvider>
    </PaperProvider>
  );
};

解决方案

1. 修改Hook,返回加载状态和订阅状态

React Hook不能是异步函数,但可以在内部用async/await处理异步逻辑,同时返回加载状态和订阅状态,让组件能判断是否需要等待。同时修正useEffect的依赖数组(移除subscribed和isLoading,避免无限循环):

export const useHasSubscription = () => { 
  const [appState, setAppState] = useState(AppState.currentState);
  const [subscribed, setSubscribed] = useState(null)
  const [isLoading, setLoading] = useState(true)

  const {
    connected,
    getSubscriptions,
  } = useIAP();

  useEffect(() => {
    const checkSubscriptionStatus = async () => {
      console.log("Connected? ", connected, "App State: ", appState)
      
      if (!connected) {
        console.log('not connected')
        // 未连接时设置默认订阅状态为false
        setSubscribed(false);
        setLoading(false);
        return;
      }
      
      if (appState !== "active") {
        setLoading(false);
        return;
      }

      try {
        console.log('app is active, checking status')
        await getSubscriptions({ skus: ["sku"] });
        const res = await IapIosSk2.subscriptionStatus("sku");
        
        console.log("subscription Status: ", res);
        const isSubscribed = res.some(({state})=> 
          state === 'subscribed' || state === 'inGracePeriod'
        );
        setSubscribed(isSubscribed);
      } catch (err) {
        console.log("failed to get status: ", err);
        // 出错时设置默认订阅状态为false
        setSubscribed(false);
      } finally {
        setLoading(false);
      }
    };

    checkSubscriptionStatus();
  }, [connected, appState, getSubscriptions]);

  return { subscribed, isLoading };
};

2. 在组件中根据加载状态等待

修改AppLoaded组件,先判断加载状态,显示加载界面直到获取到确定的订阅状态:

import { ActivityIndicator, View } from 'react-native';

const AppLoaded = () => {
  const { subscribed, isLoading } = useHasSubscription();

  // 加载中时显示加载组件
  if (isLoading) {
    return (
      <PaperProvider theme={theme}>
        <SafeAreaProvider>
          <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
            <ActivityIndicator size="large" color="#0000ff" />
          </View>
        </SafeAreaProvider>
      </PaperProvider>
    );
  }

  // 此时subscribed已有确定值(true/false)
  return (
    <PaperProvider theme={theme}>
      <SafeAreaProvider>
        {subscribed ? <NavigationRoot /> : <SubscriptionScreen />}
      </SafeAreaProvider>
    </PaperProvider>
  );
};

关键说明

  • React Hook不能定义为async函数,因为Hook必须同步执行才能保证调用顺序的一致性,违反此规则会直接报错。
  • 通过在Hook内部封装异步逻辑,并返回加载状态,组件可以优雅处理等待过程,避免初始null值导致的错误渲染。
  • 错误场景下主动设置subscribed为默认值(如false),确保组件始终能拿到确定的状态值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 19:50:25