Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blur Event Does Not Fire #11788

Closed
3 of 12 tasks
TheRealMikeD opened this issue Jan 16, 2024 · 7 comments
Closed
3 of 12 tasks

Blur Event Does Not Fire #11788

TheRealMikeD opened this issue Jan 16, 2024 · 7 comments

Comments

@TheRealMikeD
Copy link

Current behavior

I set up a handler for a 'blur' event; when navigating away from the screen in question, that event never fires.

Expected behavior

The blur event handler should fire.

Reproduction

https://github.com/TheRealMikeD/BlurNoWorkie

Platform

  • Android
  • iOS
  • Web
  • Windows
  • MacOS

Packages

  • @react-navigation/bottom-tabs
  • @react-navigation/drawer
  • @react-navigation/material-top-tabs
  • @react-navigation/stack
  • @react-navigation/native-stack
  • react-native-tab-view

Environment

  • I've removed the packages that I don't use
package version
@react-navigation/native ^6.1.9
@react-navigation/native-stack ^6.9.17
react-native-safe-area-context ^4.8.2
react-native-screens ^3.29.0
react-native 0.72.6
expo ~49.0.15
node 18.19.0
npm or yarn npm 10.2.3
@gianlucalippolis
Copy link
Contributor

gianlucalippolis commented Jan 17, 2024

I did a little bit of debugging on this problem. What I realized is that the useEffect cleanup function is called before the blur event is even emitted. Currently the only way you have is to do the unsubscribe right after the blur event is emitted (although this is not a good solution). I will continue to investigate.

@TheRealMikeD
Copy link
Author

@gianlucalippolis, that's interesting. I'll tell you some other things that I found while trying to get this to work, and maybe it will shed some additional light on the issue.

First off, the reason I wanted to use the blur event is that if a user is playing a video, and navigates away from the screen while it is playing (users do the weirdest things), I want to save their progress in the video to a database so that they can start from where they left off next time they come to the screen.

I tried using the cleanup function of the useFocusEffect hook, but it seems like changing state in that function has no effect. I'm just speculating, but maybe the screen component is already unmounted by that point? In any case, updating state in the cleanup function seemed to be too late in the lifecycle.

Why am I trying to use state to update my database? Well, right now, the update is handled by a child component, and the way that the screen lets the child component know that it should do an update is that a state variable changes in the screen, which changes a prop that's passed into the child. When the child is re-rendered, it sees the different value for the prop and knows that it should update the database. I feel like this should work and is consistent with React design principles, but I might try a different architecture in which the database update gets called directly from the screen component.

Also, for what it's worth, I'm pretty sure that the blur event used to work. I updated the versions of a lot of libraries (including react-navigation) in my project recently, and it does not work anymore, but I'm pretty sure that it did before I did the updates.

@migueldaipre
Copy link
Member

In the react navigation example app it is working normally 🤨 I will try to debug

@satya164
Copy link
Member

The screen is unmounted immediately when navigating away in case of native stack, so you won't get any events.

useFocusEffect is the appropriate way to handle cleanup. But you can't update component's state in cleanup function component is unmounted at that point. However if you need to save it somewhere else other than component state then you should be able to call that function normally.

@satya164 satya164 closed this as not planned Won't fix, can't repro, duplicate, stale Jan 18, 2024
Copy link

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.

@TheRealMikeD
Copy link
Author

Okay, thanks for the explanation, @satya164. If the blur event is not supported in the native stack navigator, might I suggest that the documentation be updated? Right now, the docs say:

There are few core events such as focus, blur etc. (documented below) that work for every navigator

You can probably see how this is misleading if these events are not in fact available for every navigator.


In my particular use case, I was able to bubble up the video progress with callbacks from the child components, then use the useFocusEffect cleanup function to save the last position to the database, as you suggested. Thanks!

@sherriff93
Copy link

sherriff93 commented Sep 18, 2024

Hi guys, I think this still isn't fixed i.e. having tested, it seems like there's still no clear way to have a focus and blur listener which are cleaned up.

When using either useFocusEffect in the following way:

useFocusEffect(useCallback(() => {
    console.log("Has focused")
    
    const unsubscribeBlur = navigation.addListener('blur', () => {
      console.log("Has blurred")
    })

    return unsubscribeBlur
  }, [navigation]));

or useEffect in the following way:

useEffect(() => {
    const unsubscribeFocus = navigation.addListener('focus', () => {
      console.log("Has focused")
    })
    const unsubscribeBlur = navigation.addListener('blur', () => {
      console.log("Has blurred")
    })

    return () => {
      unsubscribeFocus()
      unsubscribeBlur()
    }
  }, [navigation]);

...The blur event doesn't run, as the cleanup runs first.

We also can't use useFocusEffect in the following way, as the cleanup will get fired when it shouldn't do (mentioned in the React Navigation docs here https://reactnavigation.org/docs/use-focus-effect/#when-to-use-focus-and-blur-events-instead)

useFocusEffect(useCallback(() => {
    console.log("Has focused")

    return () => {
      console.log("Has blurred")
    }
  }, []));

I ended up using the following, which I think will work? But I believe it's undocumented (+ could potentially be cleaner?):

useFocusEffect(useCallback(() => {
    console.log("Has focused")/* IS_DEBUG */

    const unsubscribeBlur = navigation.addListener('blur', () => {
      console.log("Has blurred")/* IS_DEBUG */
    })

    return () => InteractionManager.runAfterInteractions(unsubscribeBlur)
  }, [navigation]));

Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants