【问题标题】:How to trigger a useEffect from external function?如何从外部函数触发 useEffect?
【发布时间】:2021-07-12 03:11:12
【问题描述】:

我是 Javascript 和 React Native 的新手,我需要一些帮助来了解如何从外部函数触发 useEffect。

我的应用程序正在使用 react-native-firebase 库侦听来自 Firebase 云消息传递 (FCM) 的消息。收到后,后台处理程序将消息写入 asyncstorage,然后显示带有通知库的通知。

当应用程序打开时,它从异步存储中检索消息并使用 FlatList 呈现它们。当应用程序首次打开并呈现列表时,这非常有效。

问题是,如果在应用处于前台时收到消息,处理程序正确处理传入消息并显示通知,但它无法触发刷新已经呈现的消息列表。

如何从 foregroundMessageHandler 触发 useEffect 的刷新/重新运行?

这是我用来在应用处于前台时捕获 FCM 消息的异步函数:

index.js

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

  await _handleNewMessage(remoteMessage);  // Inserts FCM message into asyncstorage

  await onDisplayNotification(remoteMessage);  // Displays notification

  // ...how to trigger a refresh of the useEffect from here??

});

这就是我在 UI 中呈现来自 asyncstorage 的消息的方式:

app.js

function HomeScreen() {

  const [isLoading, setLoading] = useState(true);
  const [data, setData] = useState(false);

  useEffect(() => {

      // Function to retrieve historical messages from AsyncStorage
      async function _retrieveData() {
        try {
          const value = await AsyncStorage.getItem('messages');
          if (value !== null) {

            const oldMessages = JSON.parse(value);

            setData(oldMessages);
            setLoading(false);

          } else {

            setData(false);
            setLoading(false);

          }
        } catch (error) {}
      }

      _retrieveData();  // Retrieve old messages from asyncstorage

  },[]);



function renderMessagesFlatList(data) {

  if (isLoading) {

    return (
      <ActivityIndicator />
    );

  } else if (data) {

  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)} ago</Text>
            </View>
          </View>
          <Text style={{ paddingTop: 4 }}>{item.body}</Text>
          <Divider style={{ backgroundColor: '#e7e5e5', height: 2, marginTop: 5, marginBottom: 5 }} />
          </>
        )}
    />
  );

  } else {

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

  };

};

更新:使用下面的代码,我成功地在收到消息时刷新了 FlatList 组件。但似乎每次 useEffect 触发时,它都会创建另一个前台侦听器,从而导致后续消息的重复通知。我需要弄清楚如何阻止创建这些重复的前台侦听器。

function HomeScreen() {

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

useEffect(async () => {

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

  // ...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 => {

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

    // Display notification to user
    onDisplayNotification(remoteMessage);

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

  });

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

}, [refreshPage]);

【问题讨论】:

    标签: react-native use-effect react-native-firebase


    【解决方案1】:

    您的问题有多种解决方案,但这是我希望解决的方法。

    您可以向您的HomeScreen 组件添加另一个messaging().onMessage 侦听器,并且每当收到新消息时,您只需通过再次检索数据来刷新您的列表:

    import messaging from '@react-native-firebase/messaging';
    
    function HomeScreen() {
      // ...
    
      useEffect(() => {
        // Function to retrieve historical messages from AsyncStorage
        async function _retrieveData() {
          try {
            const value = await AsyncStorage.getItem('messages');
            if (value !== null) {
              const oldMessages = JSON.parse(value);
    
              setData(oldMessages);
              setLoading(false);
            } else {
              setData(false);
              setLoading(false);
            }
          } catch (error) {}
        }
    
        _retrieveData(); // Retrieve old messages from asyncstorage
    
        // Foreground push notifications listener
        const unsubscribeOnMessage = messaging().onMessage(() => {
          // Whenever a new push notification is received, call the _retrieveData function again
          _retrieveData();
        });
    
        return () => {
          unsubscribeOnMessage();
        };
      }, []);
    
      // ...
    }
    

    更新

    1. 您可以将foregroundMessageHandler 排除在HomeScreen 组件(您的初始代码)之外。拥有多个 messaging.onMessage 侦听器是有效的。
    2. 您可以在messaging.onMessage 侦听器中的HomeScreen 组件中使用setData,而不是使用const [refreshPage, setRefreshPage] = useState(false);。所以解决方案可能是这样的:

    index.js

    const foregroundMessageHandler = messaging().onMessage(async remoteMessage => {
    
      await _handleNewMessage(remoteMessage);  // Inserts FCM message into asyncstorage
    
      await onDisplayNotification(remoteMessage);  // Displays notification
    
      // ...how to trigger a refresh of the useEffect from here??
    
    });
    

    app.js

    import messaging from '@react-native-firebase/messaging';
    
    function HomeScreen() {
      // ...
      const [isLoading, setLoading] = useState(true);
      const [data, setData] = useState(false);
    
      useEffect(() => {
        // ... Other code
       
        const unsubscribeOnMessage = messaging().onMessage((remoteMessage) => {
          // use setData to update your data for FlatList. It could be used to just append the new message to the current data
          setData((data) => [...data, remoteMessage]);
        });
    
        return () => {
          unsubscribeOnMessage();
        };
        // There shouldn't be any dependencies here
      }, []);
    
      // ...
    }
    

    【讨论】:

    • 感谢您抽出宝贵时间回复。很好的解决方案,我很自责,我没想过这样做!
    • 嗨@kapobajza,不幸的是,将messaging().onMessage 移动到HomeScreen 组件中会导致它多次触发。在 HomeScreen 之外完全相同的代码只会触发一次。
    • 你的useEffect 依赖数组中有依赖吗? useEffect(() =&gt; { //... }, [dependencies]);
    • 我认为问题在于每次收到 FCM 消息并触发 useEffect 时,它最终都会创建第二个前台侦听器,从而导致后续消息的重复通知。我不认为 unsubscribeOnMessage();正在删除现有的前台侦听器。
    • 您是否在HomeScreenmessaging().onMessage 回调中调用await onDisplayNotification(remoteMessage);
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 2020-06-23
    • 2020-08-15
    • 1970-01-01
    相关资源
    最近更新 更多