Expo push-уведомления – Событие срабатывает дважды (Expo Go)

Вопрос или проблема

Я создаю приложение на React-Native с использованием Expo (SDK 51), я использую Expo Go для тестирования на своем устройстве (iOS). Я скопировал код из документации по expo-notifications:

....
import { registerForPushNotificationsAsync } from "./utils/notifications";

const App = () => {
    const { user, setExpoToken, addAlert } = useAuthStore();

    const defaultTheme = Appearance.getColorScheme();
    const [theme, setTheme] = useState(defaultTheme);

    const toggleTheme = () => {
        const nextTheme = theme === "light" ? "dark" : "light";
        setTheme(nextTheme);
    };

    StatusBar.setBarStyle("dark-content", true);

    const notificationListener = useRef();
    const responseListener = useRef();

    useEffect(() => {
        registerForPushNotificationsAsync()
            .then((token) => {
                setExpoToken(token ?? null);
            })
            .catch((error) => setExpoToken(null));

        // Когда уведомление получено устройством

        notificationListener.current =
            Notifications.addNotificationReceivedListener((notification) => {
                console.log(notification);
                /*let { body, data, title } = notification.request.content;
                addAlert({
                    ...data,
                    title,
                    body,
                    seen: false,
                });*/
            });

        // Когда пользователь взаимодействует с уведомлением (например, нажимает на него)

        responseListener.current =
            Notifications.addNotificationResponseReceivedListener(
                (response) => {
                    console.log("rr" + response);
                    /* Todo */
                }
            );

        return () => {
            notificationListener.current &&
                Notifications.removeNotificationSubscription(
                    notificationListener.current
                );
            responseListener.current &&
                Notifications.removeNotificationSubscription(
                    responseListener.current
                );
        };
    }, []);

    return (
        <SafeAreaProvider>
            <ThemeContext.Provider value={{ theme, toggleTheme }}>
                <NavigationContainer>
                    {user ? <AppScreen /> : <LoginScreen />}
                </NavigationContainer>
            </ThemeContext.Provider>
        </SafeAreaProvider>
    );
};

export default App;

notifications.js:

import { Platform } from "react-native";
import Constants from "expo-constants";
import * as Device from "expo-device";
import * as Notifications from "expo-notifications";

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

function handleRegistrationError(errorMessage) {
    throw new Error(errorMessage);
}

const registerForPushNotificationsAsync = async () => {
    if (Platform.OS === "android") {
        Notifications.setNotificationChannelAsync("default", {
            name: "default",
            importance: Notifications.AndroidImportance.MAX,
            vibrationPattern: [0, 250, 250, 250],
            lightColor: "#FF231F7C",
        });
    }

    if (Device.isDevice) {
        const { status: existingStatus } =
            await Notifications.getPermissionsAsync();
        let finalStatus = existingStatus;
        if (existingStatus !== "granted") {
            const { status } = await Notifications.requestPermissionsAsync();
            finalStatus = status;
        }
        if (finalStatus !== "granted") {
            handleRegistrationError(
                "Разрешение не предоставлено для получения push-токена для push-уведомления!"
            );
            return;
        }
        const projectId =
            Constants?.expoConfig?.extra?.eas?.projectId ??
            Constants?.easConfig?.projectId;
        if (!projectId) {
            handleRegistrationError(
                "Ошибка конфигурации. отсутствует projectId"
            );
        }
        try {
            const pushTokenString = (
                await Notifications.getExpoPushTokenAsync({
                    projectId,
                })
            ).data;
            return pushTokenString;
        } catch (e) {
            handleRegistrationError(`${e}`);
        }
    }
};

export { registerForPushNotificationsAsync };

addNotificationReceivedListener всегда вызывается дважды, когда я получаю push-уведомление. addNotificationResponseReceivedListener работает как ожидалось.

Проблема возникает на iOS и Android, так что я предполагаю, что что-то не так с моим кодом, но не могу это решить… Что я делаю не так?

Ответ или решение

Проблема с дважды вызываемым addNotificationReceivedListener в Expo Push Notifications

Ваша проблема, когда addNotificationReceivedListener вызывается дважды при получении push-уведомления в вашем приложении React Native на Expo, может быть вызвана несколькими факторами. Давайте разберем код и возможные решения этой проблемы.

1. Проверка многократного добавления слушателей

Первым шагом к решению вашей проблемы будет удостоверение в том, что addNotificationReceivedListener не вызывается дважды. Ваша реализация слушателей находится внутри хуков useEffect, и в этом контексте важно убедиться, что хук исполняется только один раз.

Убедитесь, что вы не вызываете ваш компонент App несколько раз, что может привести к повторной регистрации слушателей. Это можно проверить, например, отладкой или добавлением console.log в начале функции App.

2. Убедитесь в отсутствии утечек памяти

Вы также можете удостовериться, что ваши слушатели корректно удаляются. В вашем return выражении внутри useEffect, которое удаляет слушателей, вы это делаете правильно:

return () => {
    notificationListener.current &&
        Notifications.removeNotificationSubscription(
            notificationListener.current
        );
    responseListener.current &&
        Notifications.removeNotificationSubscription(
            responseListener.current
        );
};

Теперь убедитесь, что этот код действительно вызывается, когда компонент размонтируется. Вы можете добавить console.log в этот блок, чтобы удостовериться, что он вызывается.

3. Использование правильных версий библиотеки

Изучайте, используете ли вы самую последнюю версию expo-notifications, которую вы можете обновить, если это необходимо. Иногда новые версии библиотек содержат исправления для известных ошибок. Также проверьте, правильно ли вы установили SDK 51, так как это может также влиять на поведение вашей программы.

4. Проверка конфигурации проекта

Убедитесь, что ваша конфигурация проекта корректна, в частности проверьте значение ProjectId. Неправильные или отсутствующие параметры могут привести к неожиданным результатам в работе уведомлений.

5. Логи и отладка

Попробуйте также добавить больше логов в вашу функцию обратного вызова notificationListener.current:

notificationListener.current = Notifications.addNotificationReceivedListener((notification) => {
    console.log("Получено уведомление:", notification);
    // Остальной код ...
});

Так вы сможете лучше понять, какое именно уведомление приходит и когда именно оно обрабатывается.

Заключение

В итоге, причина проблемы с двойным вызовом addNotificationReceivedListener может заключаться в том, что слушатели добавляются несколько раз, либо в каких-то других конфликтах в коде. Проверьте, что ваши компоненты не рендерятся повторно, и следите за правильным удалением слушателей при размонтировании компонента. Регулярно обновляйте зависимости и следите за изменениями в документации Expo, что может помочь устранить некоторые проблемы.

Если после всех этих шагов проблема останется, рекомендуется обратить внимание на дополнительные инструменты отладки, включая использование Profiler в React или встроенных инструментов разработки в Expo.

Оцените материал
Добавить комментарий

Капча загружается...