react-native-screens: enableFreeze cause useIsFocused stop working

Description

In bottom tabs, when enabling enableFreeze, useIsFocused is not triggered when screen blur.

Screenshots

Steps To Reproduce

  1. Create tabs with 2 screens Home and Settings
  2. In Settings, add const isFocused = useIsFocused()
  3. Navigate to Settings screen, isFocused updated to true (this is correct)
  4. Navigate back to Home, isFocused still true

Expected behavior

At step 4, isFocused in Settings should be false

Actual behavior

At step 4, isFocused in Settings is true

Reproduction

Platform

I just checked on iOS, not sure about other platforms

  • iOS
  • Android
  • Web
  • Windows
  • tvOS

Workflow

  • Managed workflow
  • Bare workflow

Package versions

package version
react-native 0.66.1
@react-navigation/native 6.0.6
@react-navigation/native-stack 6.2.5
@react-navigation/bottom-tabs 6.0.9
react-native-screens 3.9.0

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 3
  • Comments: 19 (6 by maintainers)

Commits related to this issue

Most upvoted comments

Hi @Titozzz, @r0b0t3d, @nandorojo, @marhaupe, and @tomasmozeris

Thank you so much for participating in this discussion. It helped a lot!

I think we’ve finally figured out how to overcome this issue and the solution is quite simple. We disable freeze for the first render so the useIsFocused can correctly pick up that screens have changed.

Could you guys confirm the solution works by testing the code in your app eg. by installing react-native-screens directly from the branch

yarn add git+https://github.com/software-mansion/react-native-screens#@kacperkapusciak/disable-freeze-for-first-render

or swapping the whole index.native.tsx file in your node_modules/react-native-screens?

That would be amazing! 🙌 Thanks!

Finally got around to opening up a PR for this here: https://github.com/software-mansion/react-native-screens/pull/1980

@amadeus your fix is also working for us. Do you want to open a PR maybe?

Looking at that code for the delayed freeze, it’s actually not concurrent safe (executing side effects in a render function, that could get thrown away). A more proper fix would be something like this:

diff --git a/node_modules/react-native-screens/src/index.native.tsx b/node_modules/react-native-screens/src/index.native.tsx
index edcd032..78aba2b 100644
--- a/node_modules/react-native-screens/src/index.native.tsx
+++ b/node_modules/react-native-screens/src/index.native.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useEffect} from 'react';
 import {
   Animated,
   Image,
@@ -152,13 +152,11 @@ function DelayedFreeze({ freeze, children }: FreezeWrapperProps) {
   // flag used for determining whether freeze should be enabled
   const [freezeState, setFreezeState] = React.useState(false);
 
-  if (freeze !== freezeState) {
-    // setImmediate is executed at the end of the JS execution block.
-    // Used here for changing the state right after the render.
+  useEffect(() => {
     setImmediate(() => {
       setFreezeState(freeze);
     });
-  }
+  }, [freeze])
 
   return <Freeze freeze={freeze ? freezeState : false}>{children}</Freeze>;
 }

FWIW: This does fix the problem for us with Native Stacks and useIsFocused firing properly

Any one noticed that it’s not working with stack, it’s solved for me with tabs but not stack after navigating away focus hook doesn’t trigger in the previous screen I tried to deactivate camera on previous screen after navigating away with focus, but didn’t work