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

React Native Expo项目中registerForPushNotificationsAsync未定义错误排查

错误原因与解决方案

错误核心原因

你的useNotifications Hook被定义成了异步函数(async),而React Hook规则明确要求:Hook必须是同步函数。当你调用useNotifications()时,它返回的是一个Promise对象,而非包含registerForPushNotificationsAsync的普通对象,因此解构时无法获取到该函数,导致undefined报错。

另外,Hook顶层不能直接使用await(你代码里写了const user= await AsyncStorage.getItem('user');),这同样违反Hook规则,会引发组件渲染异常。

修复步骤

1. 修正useNotifications的定义

去掉async关键字,将异步操作移到内部函数中处理:

// useNotifications.tsx
import { Alert, Linking, Platform } from "react-native";
import * as Device from "expo-device";
import * as Notifications from "expo-notifications";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { openSettings } from "expo-linking";
import { useMutation, gql } from '@apollo/client';

const REGISTER_DEVICE_MUTATION = gql`
mutation registerDevice($input: DeviceRegisterInput!) { 
   registerDevice(input: $input) {
     token
  }
}
`;

const UNREGISTER_DEVICE_MUTATION = gql`
mutation unregisterDevice($token: NEString!) { 
    unregisterDevice(token: $token) {
    token
  }
}
`;

// 移除async关键字,符合Hook规则
export const useNotifications = () => {
  const [registerDevice, { data: registerDeviceData, error, loading }] = useMutation(REGISTER_DEVICE_MUTATION);
  const [unregisterDevice, { data: unregisterDeviceData, error:unregisterDeviceError, loading:unregisterDeviceLoading }] = useMutation(UNREGISTER_DEVICE_MUTATION);

  const registerForPushNotificationsAsync = async (alertUser?: boolean) => {
    // 将获取user的逻辑移到该异步函数内部
    const user = await AsyncStorage.getItem('user');
    if (Device.isDevice) {
      if (!user) return; 
      const { status: existingStatus } = await Notifications.getPermissionsAsync();
      let finalStatus = existingStatus;
      if (existingStatus !== "granted") {
        const { status } = await Notifications.requestPermissionsAsync();
        finalStatus = status;
      }

      if (finalStatus !== "granted") {
        if (alertUser) {
          Alert.alert(
            "Error",
            "To enable Push Notifications please change your settings.",
            [
              { text: "OK" },
              { text: "Open Settings", onPress: openSettings },
            ]
          );
        }
        return; // 权限未授予时提前终止流程
      }
      const token = (await Notifications.getExpoPushTokenAsync()).data;
      const previousToken = await AsyncStorage.getItem('PushToken');

      if (previousToken && previousToken === token) {
        console.log('Push Token exist');
        return;
      }
  
      if (previousToken && previousToken !== token) {
        await unregisterDevice({ variables: { token: previousToken } });
        console.log('Push Token update');
      }

      // 修正mutation变量格式:按照后端定义包装成input对象
      await registerDevice({ 
        variables: { 
          input: { userId: user, token: token } 
        } 
      });
      
      // 存储推送token
      AsyncStorage.setItem('PushToken', token);                 

      if (Platform.OS === "android") {
        Notifications.setNotificationChannelAsync("default", {
          name: "default",
          importance: Notifications.AndroidImportance.MAX,
          vibrationPattern: [0, 250, 250, 250],
          lightColor: "#FF231F7C",
        });
      }
    } else {
      alert("Must use physical device for Push Notifications");
    }
  };

  const handleNotification = (notification: Notifications.Notification) => {
    // 自定义前台通知处理逻辑
  };

  const handleNotificationResponse = (
    response: Notifications.NotificationResponse
  ) => {
    const data: { url?: string } = response.notification.request.content.data;
    if (data?.url) Linking.openURL(data.url);
  };

  return {
    registerForPushNotificationsAsync,
    handleNotification,
    handleNotificationResponse,
  };
};

2. 修正navigation.tsx中的调用逻辑

确保useNotifications()在组件顶层调用,并为异步函数添加错误处理:

// navigation.tsx
function RootNavigator() {
  const { navigate } = useNavigation<StackNavigationProp<RootStackParamList>>()
  // 现在可正常解构出所需函数
  const {registerForPushNotificationsAsync, handleNotificationResponse} = useNotifications();

  useEffect(() => {
    // 封装异步逻辑,添加错误捕获
    const setupNotifications = async () => {
      try {
        await registerForPushNotificationsAsync(true);
      } catch (err) {
        console.error('Failed to register push notifications:', err);
      }
    };

    setupNotifications();

    Notifications.setNotificationHandler({
      handleNotification: async () => ({
        shouldShowAlert: true,
        shouldPlaySound: true,
        shouldSetBadge: true,
      }),
    });

    const responseListener = Notifications.addNotificationResponseReceivedListener(
      handleNotificationResponse
    );

    return () => {
      Notifications.removeNotificationSubscription(responseListener);
    };
  }, [registerForPushNotificationsAsync, handleNotificationResponse]); // 添加Hook依赖项

  // ... 其他组件代码
}

额外注意事项

  • 严格遵守Hook规则:所有Hook必须在组件顶层调用,不能在循环、条件或嵌套函数中执行,且不能定义为异步函数。
  • GraphQL变量匹配:确保前端传递的mutation变量与后端定义的输入类型完全一致,避免因格式不匹配导致接口报错。
  • 错误处理:异步操作建议添加try/catch捕获异常,便于快速定位权限、网络等问题。

内容的提问来源于stack exchange,提问作者pelotador.1

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 01:02:55