FCM - messaging().onMessage 重复回调 - 不使用 useEffect 卸载

FCM - messaging().onMessage duplicate callbacks - does not unmount with useEffect

我正在使用 FCM 将消息发送到 Android 应用程序,然后应用程序将消息呈现到 FlatList 组件中。当我收到新的 FCM 消息时,我想刷新 FlatList 组件,以便在列表中显示最新消息。我正在使用 useEffect 通过 setRefreshPage() 触发刷新。

下面的代码工作得很好,除了每次收到 FCM 消息并触发 useEffect 时,它似乎会创建第二个前台侦听器,这会导致后续消息的重复通知。我不认为 unsubscribeOnMessage();正在删除 useEffect 中现有的前台侦听器。

回调每次都加倍,即在收到第一个消息时有 1 个 FCM 消息回调,然后是下一个消息的 2 个回调,下一个消息的 4 个回调,等等。这让我想到了额外的 messaging().onMessage 前台侦听器正在每个 useEffect 触发器上创建。

触发 useEffect 时删除消息监听器的正确方法是什么?

到目前为止,我只在 Android 上测试过这段代码。

function HomeScreen() {

const [data, setData] = useState([]);
const [refreshPage, setRefreshPage] = useState(false);

useEffect(async () => {

  const value = await AsyncStorage.getItem('storedMessages');

  // ...functions to retrieve and sort historical messages into array "sortedInput"

  setData(sortedInput);  // setState to provide initial data for FlatList component

  };

  const unsubscribeOnMessage = messaging().onMessage(async remoteMessage => {

    console.log("DEBUG: Received FCM message: " + JSON.stringify(remoteMessage));

    // Function to process new message and insert into local storage
    await _handleNewMessage(remoteMessage);

    // Display notification to user
    await onDisplayNotification(remoteMessage);

    // Trigger refresh of FlatList component with setState
    setRefreshPage(Math.random() * 100);

  });

  return () => {
    unsubscribeOnMessage();
  };

}, [refreshPage]);


// Render messages into FlatList view
function renderMessagesFlatList(data) {

  if (isLoading) {

    return (
      <ActivityIndicator
        size="large"
        color="#0000ff"
        />
    );

  } else if (data.length === 0) {

    return (
    <Text>No notifications to display</Text>
    );

    } else {

  return (
  <FlatList
      data={data}
      keyExtractor={(item, index) => item.id}
      renderItem={({ item }) => (
          <>
          <View style={{ flexDirection: 'row', flex: 1 }}>
          <Image
            style={{ width: 40, height: 40 }}
            source={{uri:item.icon}}
          />
            <View style={{ flex: 1, paddingLeft: 5 }}>
            <Text style={{ color: '#000000', fontWeight: 'bold' }}>{item.sender}</Text>
            <Text>{timeSince(item.time)}</Text>
            </View>
          </View>
          <Text style={{ paddingTop: 4 }}>{item.body}</Text>
          <Divider style={{ backgroundColor: '#e7e5e5', height: 2, marginTop: 5, marginBottom: 5 }} />
          </>
        )}
    />
  );

  }
};


  return (
      <View style={{ margin: 5 }} >
      {console.log("DEBUG: isLoading = " + isLoading)}
      {console.log("DEBUG: data = " + JSON.stringify(data))}

      {renderMessagesFlatList(data)}

      </View>
  );

};

这是我每分钟发送一条 FCM 消息时最终在日志文件中看到的内容:

04-22 08:40:00.000 ReactNativeJS: DEBUG: Received FCM message: {...}

04-22 08:41:00.000 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:41:00.006 ReactNativeJS: DEBUG: Received FCM message: {...}

04-22 08:42:00.000 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:42:00.004 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:42:00.009 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:42:00.014 ReactNativeJS: DEBUG: Received FCM message: {...}

04-22 08:43:00.000 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:43:00.006 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:43:00.011 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:43:00.017 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:43:00.021 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:43:00.028 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:43:00.036 ReactNativeJS: DEBUG: Received FCM message: {...}
04-22 08:43:00.049 ReactNativeJS: DEBUG: Received FCM message: {...}

由于这 2 行,您正在让 FCM 重复更新,导致循环,这是一种反模式。

setRefreshPage(Math.random() * 100);  //<-- You are updating the state refreshPage


}, [refreshPage]); // <--- You use refreshPage as a dependency means useEffect will be called. And then you will setRefreshPage again

我无法为您提供更多建议,因为我不确定您为什么要执行 setRefreshPage。如果您希望它只在页面加载时触发一次,那么您可以删除 refreshPage 依赖项。