Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions FabricTestExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import Test1726 from './src/Test1726';
import Test1802 from './src/Test1802';
import Test1844 from './src/Test1844';
import Test1864 from './src/Test1864';
import Test1978 from './src/Test1978';

enableFreeze(true);

Expand Down
90 changes: 90 additions & 0 deletions FabricTestExample/src/Test1978.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { NavigationContainer } from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
} from '@react-navigation/native-stack';
import React, { useState } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';

type StackParamList = {
Home: undefined;
Home1: undefined;
Home2: undefined;
Home3: undefined;
};

interface MainScreenProps {
navigation: NativeStackNavigationProp<StackParamList>;
}

const Home = ({ navigation }: MainScreenProps) => (
<View style={styles.view}>
<Text onPress={() => navigation.navigate('Home1')}>
This is the initial View
</Text>
</View>
);

const Home1 = () => (
<View style={styles.view}>
<Text>This is View 1</Text>
</View>
);

const Home2 = ({ navigation }: MainScreenProps) => (
<View style={styles.view}>
<Text onPress={() => navigation.navigate('Home3')}>This is View 2</Text>
</View>
);

const Home3 = () => (
<View style={styles.view}>
<Text>This is View 3</Text>
</View>
);

const Stack = createNativeStackNavigator();

const Test1978 = () => {
const [hasChangedState, setHasChangedState] = useState(true);

return (
<NavigationContainer>
<Stack.Navigator>
{hasChangedState ? (
<>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Home1" component={Home1} />
</>
) : (
<>
<Stack.Screen name="Home2" component={Home2} />
<Stack.Screen name="Home3" component={Home3} />
</>
)}
</Stack.Navigator>
<TouchableOpacity
style={styles.button}
onPress={() => setHasChangedState(old => !old)}>
<Text>Change state</Text>
</TouchableOpacity>
</NavigationContainer>
);
};

const styles = StyleSheet.create({
button: {
justifyContent: 'center',
alignItems: 'center',
height: 100,
},
view: {
alignItems: 'center',
backgroundColor: '#b7c4bb',
flex: 1,
justifyContent: 'center',
padding: 12,
},
});

export default Test1978;
1 change: 1 addition & 0 deletions TestsExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ import Test1791 from './src/Test1791';
import Test1802 from './src/Test1802';
import Test1844 from './src/Test1844';
import Test1864 from './src/Test1864';
import Test1978 from './src/Test1978';

enableFreeze(true);

Expand Down
90 changes: 90 additions & 0 deletions TestsExample/src/Test1978.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { NavigationContainer } from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
} from '@react-navigation/native-stack';
import React, { useState } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';

type StackParamList = {
Home: undefined;
Home1: undefined;
Home2: undefined;
Home3: undefined;
};

interface MainScreenProps {
navigation: NativeStackNavigationProp<StackParamList>;
}

const Home = ({ navigation }: MainScreenProps) => (
<View style={styles.view}>
<Text onPress={() => navigation.navigate('Home1')}>
This is the initial View
</Text>
</View>
);

const Home1 = () => (
<View style={styles.view}>
<Text>This is View 1</Text>
</View>
);

const Home2 = ({ navigation }: MainScreenProps) => (
<View style={styles.view}>
<Text onPress={() => navigation.navigate('Home3')}>This is View 2</Text>
</View>
);

const Home3 = () => (
<View style={styles.view}>
<Text>This is View 3</Text>
</View>
);

const Stack = createNativeStackNavigator();

const Test1978 = () => {
const [hasChangedState, setHasChangedState] = useState(true);

return (
<NavigationContainer>
<Stack.Navigator>
{hasChangedState ? (
<>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Home1" component={Home1} />
</>
) : (
<>
<Stack.Screen name="Home2" component={Home2} />
<Stack.Screen name="Home3" component={Home3} />
</>
)}
</Stack.Navigator>
<TouchableOpacity
style={styles.button}
onPress={() => setHasChangedState(old => !old)}>
<Text>Change state</Text>
</TouchableOpacity>
</NavigationContainer>
);
};

const styles = StyleSheet.create({
button: {
justifyContent: 'center',
alignItems: 'center',
height: 100,
},
view: {
alignItems: 'center',
backgroundColor: '#b7c4bb',
flex: 1,
justifyContent: 'center',
padding: 12,
},
});

export default Test1978;
24 changes: 19 additions & 5 deletions ios/RNSScreenStack.mm
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ @implementation RNSScreenStackView {
BOOL _hasLayout;
__weak RNSScreenStackManager *_manager;
BOOL _updateScheduled;
BOOL _isViewRecycled;
#ifdef RCT_NEW_ARCH_ENABLED
UIView *_snapshot;
#endif
Expand Down Expand Up @@ -454,8 +455,8 @@ - (void)setModalViewControllers:(NSArray<UIViewController *> *)controllers

- (void)setPushViewControllers:(NSArray<UIViewController *> *)controllers
{
// when there is no change we return immediately
if ([_controller.viewControllers isEqualToArray:controllers]) {
// when there is no change and the view of the controller is not recycled we return immediately
if ([_controller.viewControllers isEqualToArray:controllers] && !_isViewRecycled) {
return;
}

Expand All @@ -467,7 +468,7 @@ - (void)setPushViewControllers:(NSArray<UIViewController *> *)controllers
// when transition is ongoing, any updates made to the controller will not be reflected until the
// transition is complete. In particular, when we push/pop view controllers we expect viewControllers
// property to be updated immediately. Based on that property we then calculate future updates.
// When the transition is ongoing the property won't be updated immediatly. We therefore avoid
// When the transition is ongoing the property won't be updated immediately. We therefore avoid
// making any updated when transition is ongoing and schedule updates for when the transition
// is complete.
if (_controller.transitionCoordinator != nil) {
Expand Down Expand Up @@ -978,10 +979,23 @@ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childCompone
@(index),
@([childComponentView.superview tag]));

[_reactSubviews insertObject:(RNSScreenView *)childComponentView atIndex:index];
((RNSScreenView *)childComponentView).reactSuperview = self;
RNSScreenView *screenChildComponent = (RNSScreenView *)childComponentView;
[_reactSubviews insertObject:screenChildComponent atIndex:index];

// Since the view may be recycled we don't want to remount snapshot to the hierarchy.
// Thus, we want to reset view to initial view and force `setPushViewControllers` to be called.
if ([NSStringFromClass([screenChildComponent.controller.view class]) isEqualToString:@"_UIReplicantView"]) {
[screenChildComponent.controller resetViewToScreen];
_isViewRecycled = YES;
}

screenChildComponent.reactSuperview = self;

dispatch_async(dispatch_get_main_queue(), ^{
[self maybeAddToParentAndUpdateContainer];
// We don't need to indicate if the view has been recycled, since we're after the mounting state of the
// recycled view.
self->_isViewRecycled = NO;
});
}

Expand Down