Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React navigation event listeners being called multiple times

I'm trying to implement some listeners on react navigation using a functional component approach:

const ExampleComponent = () => {

  const [subs, setSubs] = React.useState([]); 
  React.useEffect(() => {
    setSubs([
      navigation.addListener('willFocus', () => console.log('will focus')),
      navigation.addListener('willBlur', () => console.log('will blur')),
      navigation.addListener('didFocus', () => console.log('did focus')),
      navigation.addListener('didBlur', () => console.log('did blur')),
    ]);

    return () => {
      setSubs([]) 
    }
  }, [])

  return (
    ...
  )
};

Following the advice: https://fantashit.com/navigation-listeners-firing-multiple-times-per-event/

However what seems to be happening is each listener is being called twice, and I'm unsure how to fix this?

like image 763
barte Avatar asked Jun 07 '20 18:06

barte


2 Answers

It is fact for react native, whenever you add listeners for any mounted screen, the listeners are still active after screen is unmounted. And whenever such listeners try to change state of an unmounted screen, react native will generate warning.
so always try to add listeners whenever you mount screen and remove the same while unmounting

const ExampleComponent = () => {

  const [subs, setSubs] = React.useState([]); 
  React.useEffect(() => {
    setSubs([
      navigation.addListener('willFocus', () => console.log('will focus')),
      navigation.addListener('willBlur', () => console.log('will blur')),
      navigation.addListener('didFocus', () => console.log('did focus')),
      navigation.addListener('didBlur', () => console.log('did blur')),
    ]);

    const unsubscribe = () =>{
    navigation.removeAllListeners();
    }
    // Remove all listeners, because there have to be no listeners on unmounted screen
    return () => unsubscribe();
  }, [])

same can be achieved by the following in react navigation


useFocusEffect(
    React.useCallback(() =>{

      // Listening on the refresh event
       RefreshEventEmitter.addListener("refresh", () => {
         console.log("Refresh event emitted");
       });

      // ----------------------------------------------
      // Removing all the listeners, so that screen does not re-render after unmounting..
      const unsubscribe = () => {
        RefreshEventEmitter.removeAllListeners();
      };

 
      // Returned function is called when screen is unmounted from display!!
      return () => unsubscribe();
    }, [])
  );

useFocusEffect is called once when screen is mounted to the display and returned function by the same is called when screen is unmounted from the display.

like image 171
gaurav Avatar answered Oct 20 '22 09:10

gaurav


As docs say:

addListener returns a function that can be called to unsubscribe from the event.

Try cleaning up each subscription in cleanup function:

React.useEffect(() => {
  const unSubs = [
    navigation.addListener('willFocus', () => console.log('will focus')),
    navigation.addListener('willBlur', () => console.log('will blur')),
    navigation.addListener('didFocus', () => console.log('did focus')),
    navigation.addListener('didBlur', () => console.log('did blur')),
  ]

  return function cleanup() {
    unSubs.forEach((unSub) => {
      unSub()
    })
  }
}, [])
like image 7
Ajeet Shah Avatar answered Oct 20 '22 10:10

Ajeet Shah