Skip to content

Commit b26631a

Browse files
j-piaseckija1ns
authored andcommitted
fix(Android): avoid race condition related to state on the new arch (software-mansion#2024)
## Description Fixes a race condition that is causing the layout of the screen to be wrong when the window size is changed. The flow before this change was: - window changes size (in this case keyboard is opened) - layout is done, `onLayout` gets called in the `Screen` class - frame is different than the one in the current state (window height is reduced by the keyboard's height) - native state is updated **asynchronously** - window changes size again (in this case keyboard is closed) - layout is done, `onLayout` gets called in the `Screen` class - since the native state update hasn't been applied yet at this point, the new frame is the same as the one stored in the native state, native state update is skipped - native state update is applied (the one with reduced height), shadow node size gets changed ## Changes - Fixed a race condition that resulted in the screen's children having wrong dimensions when the window size was changed <!-- ## Screenshots / GIFs Here you can add screenshots / GIFs documenting your change. You can add before / after section if you're changing some behavior. --> ### Before https://github.com/software-mansion/react-native-screens/assets/21055725/eec27502-904e-461f-8762-21e24c9e0e8f ### After https://github.com/software-mansion/react-native-screens/assets/21055725/aba69232-dd09-46b0-9063-09e090acdff4 (You may need to look at this one frame-by-frame. In some cases there is one frame where the layout looks like in the `before` video, but it gets corrected after. ## Test code and steps to reproduce ```jsx import 'react-native-gesture-handler'; import {NavigationContainer} from '@react-navigation/native'; import React, {useRef} from 'react'; import {createStackNavigator} from '@react-navigation/stack'; import {Button, TextInput, View} from 'react-native'; const Stack = createStackNavigator(); function Thing() { const ref = useRef(null); return ( <View style={{flex: 1, backgroundColor: 'wheat'}}> <TextInput ref={ref} style={{backgroundColor: 'white', borderWidth: 1, borderColor: 'black'}} /> <Button title="do the thing" onPress={() => { ref.current?.focus(); setTimeout(() => { ref.current?.blur(); }, 30); }} /> </View> ); } function App() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Home" component={Thing} /> </Stack.Navigator> </NavigationContainer> ); } export default App; ``` ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes
1 parent fff6203 commit b26631a

File tree

1 file changed

+12
-11
lines changed

1 file changed

+12
-11
lines changed

android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package com.swmansion.rnscreens
33
import android.view.ViewGroup
44
import androidx.annotation.UiThread
55
import com.facebook.react.bridge.ReactContext
6-
import com.facebook.react.bridge.ReadableMap
76
import com.facebook.react.bridge.WritableMap
87
import com.facebook.react.bridge.WritableNativeMap
98
import com.facebook.react.uimanager.FabricViewStateManager
@@ -13,6 +12,9 @@ import kotlin.math.abs
1312
abstract class FabricEnabledViewGroup constructor(context: ReactContext?) : ViewGroup(context), FabricViewStateManager.HasFabricViewStateManager {
1413
private val mFabricViewStateManager: FabricViewStateManager = FabricViewStateManager()
1514

15+
private var lastSetWidth = 0f
16+
private var lastSetHeight = 0f
17+
1618
override fun getFabricViewStateManager(): FabricViewStateManager {
1719
return mFabricViewStateManager
1820
}
@@ -28,17 +30,16 @@ abstract class FabricEnabledViewGroup constructor(context: ReactContext?) : View
2830

2931
// Check incoming state values. If they're already the correct value, return early to prevent
3032
// infinite UpdateState/SetState loop.
31-
val currentState: ReadableMap? = mFabricViewStateManager.getStateData()
32-
if (currentState != null) {
33-
val delta = 0.9f
34-
val stateFrameHeight: Float = if (currentState.hasKey("frameHeight")) currentState.getDouble("frameHeight").toFloat() else 0f
35-
val stateFrameWidth: Float = if (currentState.hasKey("frameWidth")) currentState.getDouble("frameWidth").toFloat() else 0f
36-
if (abs(stateFrameWidth - realWidth) < delta &&
37-
abs(stateFrameHeight - realHeight) < delta
38-
) {
39-
return
40-
}
33+
val delta = 0.9f
34+
if (abs(lastSetWidth - realWidth) < delta &&
35+
abs(lastSetHeight - realHeight) < delta
36+
) {
37+
return
4138
}
39+
40+
lastSetWidth = realWidth
41+
lastSetHeight = realHeight
42+
4243
mFabricViewStateManager.setState {
4344
val map: WritableMap = WritableNativeMap()
4445
map.putDouble("frameWidth", realWidth.toDouble())

0 commit comments

Comments
 (0)