Skip to content

Commit 3d85d59

Browse files
committed
feat(bottom-tabs,iOS): change default value for controlledBottomTabs featrue flag to true (#27)
## Description Default value change for `controlledBottomTabs` flag to `true` + some cleanup & logs. - [ ] Ensured that CI passes
1 parent d88e555 commit 3d85d59

File tree

3 files changed

+55
-36
lines changed

3 files changed

+55
-36
lines changed

apps/src/tests/TestBottomTabs.tsx renamed to apps/src/tests/TestBottomTabs/index.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { Dispatch, SetStateAction, useContext } from 'react';
1+
import React, { type Dispatch, type SetStateAction, useContext } from 'react';
22
import {
33
Button,
44
type NativeSyntheticEvent,
@@ -13,7 +13,7 @@ import {
1313
enableFreeze,
1414
featureFlags,
1515
} from 'react-native-screens';
16-
import Colors from '../shared/styling/Colors';
16+
import Colors from '../../shared/styling/Colors';
1717
import { NativeFocusChangeEvent } from 'react-native-screens/fabric/BottomTabsNativeComponent';
1818

1919
enableFreeze(true);
@@ -37,8 +37,6 @@ const ConfigWrapperContext = React.createContext<ConfigWrapper>({
3737
config: defaultGlobalConfiguration,
3838
});
3939

40-
featureFlags.experiment.controlledBottomTabs = false;
41-
4240
interface LayoutViewProps extends ViewProps {
4341
tabID?: number;
4442
}
@@ -103,9 +101,13 @@ function TabContentView(props: TabContentViewProps) {
103101
<Text>
104102
heavyTabRender: {configWrapper.config.heavyTabRender ? 'true' : 'false'}
105103
</Text>
104+
<Text>
105+
controlledBottomTabs:{' '}
106+
{configWrapper.config.controlledBottomTabs ? 'true' : 'false'}
107+
</Text>
106108
<Button title="Next tab" onPress={selectNextTab} />
107109
<Button
108-
title="Toggle heavy render, current value {}"
110+
title="Toggle heavy render"
109111
onPress={() => {
110112
configWrapper.setConfig?.(prev => {
111113
return {
@@ -141,11 +143,11 @@ function BottomTabsExample() {
141143
const [focusedTab, setFocusedTab] = React.useState(0);
142144
const selectNextTab = React.useCallback(() => {
143145
setFocusedTab(old => old + 1);
144-
}, [setFocusedTab]);
146+
}, []);
145147

146148
const configWrapper = React.useContext(ConfigWrapperContext);
147149

148-
console.log(`Render: focusedTab: ${focusedTab}`);
150+
console.log(`BottomTabsExample (topLevel) render: focusedTab: ${focusedTab}`);
149151

150152
// Pending state can be used to render placeholder for the time of transition.
151153
const [_, startTransition] = React.useTransition();
@@ -155,12 +157,16 @@ function BottomTabsExample() {
155157
const tabKey = event.nativeEvent.tabKey;
156158

157159
// Use `startTransition` only if the state is controlled in JS
158-
const transitionFn = configWrapper.config.controlledBottomTabs
159-
? startTransition
160-
: (callback: () => void) => {
161-
callback();
162-
};
160+
// const transitionFn = !configWrapper.config.controlledBottomTabs
161+
// ? startTransition
162+
// : (callback: () => void) => {
163+
// callback();
164+
// };
165+
166+
const transitionFn = startTransition;
167+
163168
transitionFn(() => {
169+
console.info(`Starting transition to ${tabKey}`);
164170
if (tabKey === 'Tab1') {
165171
setFocusedTab(0);
166172
} else if (tabKey === 'Tab2') {

src/components/BottomTabsScreen.tsx

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import BottomTabsScreenNativeComponent, {
55
type NativeProps,
66
} from '../fabric/BottomTabsScreenNativeComponent';
77
import {
8-
ColorValue,
9-
NativeSyntheticEvent,
8+
type ColorValue,
9+
type NativeSyntheticEvent,
1010
StyleSheet,
11-
ViewProps,
11+
type ViewProps,
1212
findNodeHandle,
1313
} from 'react-native';
1414
import { Freeze } from 'react-freeze';
1515
import { freezeEnabled } from '../core';
16+
import { featureFlags } from '../flags';
1617

1718
export type EmptyObject = Record<string, never>;
1819
export type BottomTabsScreenEventHandler<T> = (
@@ -24,6 +25,8 @@ export interface BottomTabsScreenProps {
2425
placeholder?: React.ReactNode | undefined;
2526

2627
// Control
28+
29+
// Works only in 'controlled' mode. Otherwise this prop indicates only initally selected tab.
2730
isFocused?: boolean;
2831
tabKey: string;
2932

@@ -41,21 +44,6 @@ export interface BottomTabsScreenProps {
4144
}
4245

4346
function BottomTabsScreen(props: BottomTabsScreenProps) {
44-
const [nativeViewHasDisappeared, setNativeViewHasDisappeared] =
45-
React.useState(true);
46-
47-
const {
48-
onWillAppear,
49-
onDidAppear,
50-
onWillDisappear,
51-
onDidDisappear,
52-
isFocused = false,
53-
...propsWoEventHandlers
54-
} = props;
55-
56-
const shouldFreeze =
57-
freezeEnabled() && !isFocused && nativeViewHasDisappeared;
58-
5947
const componentNodeRef = React.useRef<React.Component<NativeProps>>(null);
6048
const componentNodeHandle = React.useRef<number>(-1);
6149

@@ -68,12 +56,37 @@ function BottomTabsScreen(props: BottomTabsScreenProps) {
6856
}
6957
}, []);
7058

59+
const [nativeViewIsVisible, setNativeViewIsVisible] = React.useState(false);
60+
61+
const {
62+
onWillAppear,
63+
onDidAppear,
64+
onWillDisappear,
65+
onDidDisappear,
66+
isFocused = false,
67+
...propsWoEventHandlers
68+
} = props;
69+
70+
let shouldFreeze = freezeEnabled();
71+
72+
if (featureFlags.experiment.controlledBottomTabs) {
73+
// If the tabs are JS controlled, we want to freeze only when given view is not focused && it is not currently visible
74+
shouldFreeze = shouldFreeze && !nativeViewIsVisible && !isFocused;
75+
console.info(
76+
`TabsScreen [${componentNodeHandle.current ?? -1}] render; tabKey: ${
77+
propsWoEventHandlers.tabKey
78+
} UPDATE DUE TO CONTROLLED TABS TO shouldFreeze: ${shouldFreeze}`,
79+
);
80+
} else {
81+
shouldFreeze = shouldFreeze && !nativeViewIsVisible;
82+
}
83+
7184
const onWillAppearCallback = React.useCallback(
7285
(event: NativeSyntheticEvent<EmptyObject>) => {
7386
console.log(
7487
`TabsScreen [${componentNodeHandle.current}] onWillAppear received`,
7588
);
76-
setNativeViewHasDisappeared(false);
89+
setNativeViewIsVisible(true);
7790
onWillAppear?.(event);
7891
},
7992
[onWillAppear],
@@ -104,16 +117,16 @@ function BottomTabsScreen(props: BottomTabsScreenProps) {
104117
console.log(
105118
`TabsScreen [${componentNodeHandle.current}] onDidDisappear received`,
106119
);
107-
setNativeViewHasDisappeared(true);
120+
setNativeViewIsVisible(false);
108121
onDidDisappear?.(event);
109122
},
110123
[onDidDisappear],
111124
);
112125

113126
console.info(
114-
`TabsScreen [${
115-
componentNodeHandle.current ?? -1
116-
}] render; shouldFreeze: ${shouldFreeze}, isFocused: ${isFocused} nativeViewHasDisappeared: ${nativeViewHasDisappeared}`,
127+
`TabsScreen [${componentNodeHandle.current ?? -1}] render; tabKey: ${
128+
propsWoEventHandlers.tabKey
129+
} shouldFreeze: ${shouldFreeze}, isFocused: ${isFocused} nativeViewIsVisible: ${nativeViewIsVisible}`,
117130
);
118131

119132
return (

src/flags.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const RNS_CONTROLLED_BOTTOM_TABS_DEFAULT = false;
1+
const RNS_CONTROLLED_BOTTOM_TABS_DEFAULT = true;
22

33
// TODO: Migrate freeze here
44

0 commit comments

Comments
 (0)