Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React navigation didfocus event listener works differently between class component and functional component

When I transition to this screen, it will do some API calls to fetch the latest data. But it seems not trigger the didFocus event to fire the api calls when I transition from another navigation stack with hooks version while it works well with class version.

How do I make hooks version have the same behavior as class version?

What's the difference between this two version?

class component version

class someScreen extends Component {
    componentDidMount() {
       const {
           navigation,
       } = this.props;

       this.navFocusListener = navigation.addListener('didFocus', () => {
         // do some API calls here
         console.log("class version");
         API_CALL();
       });
    }

    componentWillUnmount() {
        this.navFocusListener.remove();
    }
}

console output

transition from other navigation stack to this screen: class version

transition between screens in same stack: class version

Hooks version

const someScreen = ({
 navigation,
}) => {
    useEffect(() => {
        const navFocusListener = navigation.addListener('didFocus', () => {
        // do some API calls here
        API_CALL();
        console.log('hooooks');
    });

    return () => {
        navFocusListener.remove();
    };
  }, []);
}

console output

transition from other navigation stack to this screen: nothing is shown in console

transition between screens in same stack: hooooks

BTW, here is the workaround solution I found

const someScreen = ({
 navigation,
}) => {
      useEffect(() => {
          const isFocused = navigation.isFocused();

          // manually judge if the screen is focused
          // if did, fire api call
          if (isFocused) {
             // do the same API calls here
             API_CALL();
             console.log('focused section');
          }

          const navFocusListener = navigation.addListener('didFocus', () => {
              // do some API calls here
              API_CALL();
              console.log('listener section');
          });

          return () => {
              navFocusListener.remove();
          };
      }, []);
}

console output

transition from other navigation stack to this screen: focused section

transition between screens in same stack: listener section

like image 273
chitsutote Avatar asked Aug 12 '19 19:08

chitsutote


People also ask

What is difference between useEffect and useFocusEffect?

The useFocusEffect is analogous to React's useEffect hook. The only difference is that it only runs if the screen is currently focused. The effect will run whenever the dependencies passed to React.

How do I check my screen focus or not in react navigation?

React Navigation provides a hook that returns a boolean indicating whether the screen is focused or not. The hook will return true when the screen is focused and false when our component is no longer focused. This enables us to render something conditionally based on whether the user is on the screen or not.

Can I add event listener to react component?

To use the addEventListener method in function components in React: Set the ref prop on the element. Use the current property on the ref to get access to the element. Add the event listener in the useEffect hook.

How do I use navigation addListener?

navigation.addListener ​Inside a screen, you can add listeners on the navigation prop with the addListener method. The addListener method takes 2 arguments: type of the event, and a callback to be called on the event. It returns a function that can be called to unsubscribe from the event.


1 Answers

I guess I found the root cause of the inconsistent behavior. There is another hook called useLayoutEffect

useLayoutEffect The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

the useLayoutEffect will block the painting while the useEffect will not. That confirm and explains my guess that the didFocus event had fired, but it didn't trigger the listener since it miss the timing

so in my case, I have to use useLayoutEffect instead of useEffect

reference: https://kentcdodds.com/blog/useeffect-vs-uselayouteffect https://reactjs.org/docs/hooks-reference.html#uselayouteffect

like image 175
chitsutote Avatar answered Sep 25 '22 13:09

chitsutote