From 757ee4c9a3f00aaf03b6cd6cad55271f6c8bdc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Du=C5=BCy?= <91994767+alduzy@users.noreply.github.com> Date: Tue, 9 Jul 2024 09:55:52 +0200 Subject: [PATCH] fix(iOS): white flash on tab change when using native stack (#2188) ## Description This PR gets rid of undesired white flashes during `maybeAddToParentAndUpdateContainer`. The white flash was present on paper architecture when `unmountOnBlur` option was set to true on parent bottomStackNavigator (see repro). The affected logic was previously introduced or changed by following PRs: - https://github.com/software-mansion/react-native-screens/pull/600 - https://github.com/software-mansion/react-native-screens/pull/613 - https://github.com/software-mansion/react-native-screens/pull/643 The removed `_hasLayout` was initially added by https://github.com/software-mansion/react-native-screens/pull/600 in order to resolve an issue: https://github.com/software-mansion/react-native-screens/issues/432. However the logic was later changed by https://github.com/software-mansion/react-native-screens/pull/613 in order to fix another issue and the added `_hasLayout` may not fix anything eventually, as stated by [this comment](https://github.com/software-mansion/react-native-screens/issues/432#issuecomment-687868469). Fixes #1645. ## Changes - removed `_hasLayout` variable - added repros ## Screenshots / GIFs ### Before https://github.com/software-mansion/react-native-screens/assets/91994767/226a32d7-728b-48dd-b21a-6a1e4195add2 ### After https://github.com/software-mansion/react-native-screens/assets/91994767/32febcf1-d159-4a9d-ae3a-373042732a6d ## Test code and steps to reproduce - added `Test1645.js` repro to test examples - added `Test432.tsx` repro to test examples ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes --------- Co-authored-by: Kacper Kafara --- apps/test-examples/App.js | 2 + apps/test-examples/src/Test1645.js | 98 ++++++++++++++++++++++++++++++ apps/test-examples/src/Test432.tsx | 72 ++++++++++++++++++++++ ios/RNSScreenStack.mm | 18 +----- 4 files changed, 175 insertions(+), 15 deletions(-) create mode 100644 apps/test-examples/src/Test1645.js create mode 100644 apps/test-examples/src/Test432.tsx diff --git a/apps/test-examples/App.js b/apps/test-examples/App.js index a07e927bc7..8aeafe0bf1 100644 --- a/apps/test-examples/App.js +++ b/apps/test-examples/App.js @@ -8,6 +8,7 @@ import Test111 from './src/Test111'; import Test263 from './src/Test263'; import Test349 from './src/Test349'; import Test364 from './src/Test364'; +import Test432 from './src/Test432'; import Test528 from './src/Test528'; import Test550 from './src/Test550'; import Test556 from './src/Test556'; @@ -84,6 +85,7 @@ import Test1473 from './src/Test1473'; import Test1476 from './src/Test1476'; import Test1509 from './src/Test1509'; import Test1539 from './src/Test1539'; +import Test1645 from './src/Test1645'; import Test1646 from './src/Test1646'; import Test1649 from './src/Test1649'; import Test1671 from './src/Test1671'; diff --git a/apps/test-examples/src/Test1645.js b/apps/test-examples/src/Test1645.js new file mode 100644 index 0000000000..ec4a508944 --- /dev/null +++ b/apps/test-examples/src/Test1645.js @@ -0,0 +1,98 @@ +/* eslint-disable react-native/no-inline-styles */ + +import React, {useEffect, useState} from 'react'; +import {Text, View} from 'react-native'; + +import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'; +import {createNativeStackNavigator} from '@react-navigation/native-stack'; +import {createStackNavigator} from '@react-navigation/stack'; +import {NavigationContainer} from '@react-navigation/native'; + +const TestBottomTabBar = createBottomTabNavigator(); +const TestNativeStack1 = createNativeStackNavigator(); +const TestNativeStack2 = createNativeStackNavigator(); + +const TestScreen1 = () => { + const [t, setT] = useState(110); + + useEffect(() => { + const interval = setInterval(() => { + setT(lastT => lastT + 1); + }, 100); + + return () => clearInterval(interval); + }, []); + return ( + + {Array.from({length: 100}).map((e, idx) => ( + + T{idx}: {t} + + ))} + + ); +}; + +const TestScreen2 = () => { + const [t, setT] = useState(0); + + useEffect(() => { + const interval = setInterval(() => { + setT(lastT => lastT + 1); + }, 100); + + return () => clearInterval(interval); + }, []); + + return ( + + {Array.from({length: 100}).map((e, idx) => ( + + T{idx}: {t} + + ))} + + ); +}; +const TestScreenTab1 = () => { + return ( + + + + + ); +}; + +const TestScreenTab2 = () => { + return ( + + + + + ); +}; + +const App = () => { + return ( + + + + + + + ); +}; + +export default App; diff --git a/apps/test-examples/src/Test432.tsx b/apps/test-examples/src/Test432.tsx new file mode 100644 index 0000000000..5a0843dbc0 --- /dev/null +++ b/apps/test-examples/src/Test432.tsx @@ -0,0 +1,72 @@ +import { Pressable, View, Button, Text } from 'react-native'; + +import { NavigationContainer, useNavigation } from '@react-navigation/native'; +import { + NativeStackScreenProps, + createNativeStackNavigator, +} from '@react-navigation/native-stack'; +import React, { useCallback } from 'react'; + +type RootStackParamList = { + Home: undefined; + Settings: undefined; +}; +type RootStackScreenProps = + NativeStackScreenProps; +const HomeScreen = ({ navigation }: RootStackScreenProps<'Home'>) => { + const showSettings = useCallback(() => { + navigation.navigate('Settings'); + }, [navigation]); + return ( + +