Skip to content

Commit

Permalink
feat: return a NavigationContent component from useNavigationBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
satya164 committed May 9, 2021
1 parent a6e4981 commit 1179d56
Show file tree
Hide file tree
Showing 14 changed files with 246 additions and 198 deletions.
23 changes: 15 additions & 8 deletions packages/bottom-tabs/src/navigators/createBottomTabNavigator.tsx
Expand Up @@ -70,7 +70,12 @@ function BottomTabNavigator({
);
}

const { state, descriptors, navigation } = useNavigationBuilder<
const {
state,
descriptors,
navigation,
NavigationContent,
} = useNavigationBuilder<
TabNavigationState<ParamListBase>,
TabRouterOptions,
TabActionHelpers<ParamListBase>,
Expand All @@ -85,13 +90,15 @@ function BottomTabNavigator({
});

return (
<BottomTabView
{...rest}
state={state}
navigation={navigation}
descriptors={descriptors}
sceneContainerStyle={sceneContainerStyle}
/>
<NavigationContent>
<BottomTabView
{...rest}
state={state}
navigation={navigation}
descriptors={descriptors}
sceneContainerStyle={sceneContainerStyle}
/>
</NavigationContent>
);
}

Expand Down
125 changes: 61 additions & 64 deletions packages/bottom-tabs/src/views/BottomTabView.tsx
@@ -1,8 +1,7 @@
import * as React from 'react';
import { StyleSheet, Platform } from 'react-native';
import { SafeAreaInsetsContext } from 'react-native-safe-area-context';
import {
NavigationHelpersContext,
import type {
ParamListBase,
TabNavigationState,
} from '@react-navigation/native';
Expand Down Expand Up @@ -89,72 +88,70 @@ export default function BottomTabView(props: Props) {
const { routes } = state;

return (
<NavigationHelpersContext.Provider value={navigation}>
<SafeAreaProviderCompat>
<MaybeScreenContainer
enabled={detachInactiveScreens}
style={styles.container}
>
{routes.map((route, index) => {
const descriptor = descriptors[route.key];
const { lazy = true, unmountOnBlur } = descriptor.options;
const isFocused = state.index === index;
<SafeAreaProviderCompat>
<MaybeScreenContainer
enabled={detachInactiveScreens}
style={styles.container}
>
{routes.map((route, index) => {
const descriptor = descriptors[route.key];
const { lazy = true, unmountOnBlur } = descriptor.options;
const isFocused = state.index === index;

if (unmountOnBlur && !isFocused) {
return null;
}
if (unmountOnBlur && !isFocused) {
return null;
}

if (lazy && !loaded.includes(route.key) && !isFocused) {
// Don't render a lazy screen if we've never navigated to it
return null;
}
if (lazy && !loaded.includes(route.key) && !isFocused) {
// Don't render a lazy screen if we've never navigated to it
return null;
}

const {
header = ({ layout, options }: BottomTabHeaderProps) => (
<Header
{...options}
layout={layout}
title={getHeaderTitle(options, route.name)}
/>
),
} = descriptor.options;
const {
header = ({ layout, options }: BottomTabHeaderProps) => (
<Header
{...options}
layout={layout}
title={getHeaderTitle(options, route.name)}
/>
),
} = descriptor.options;

return (
<MaybeScreen
key={route.key}
style={StyleSheet.absoluteFill}
visible={isFocused}
enabled={detachInactiveScreens}
>
<BottomTabBarHeightContext.Provider value={tabBarHeight}>
<Screen
focused={isFocused}
route={descriptor.route}
navigation={descriptor.navigation}
headerShown={descriptor.options.headerShown}
headerStatusBarHeight={
descriptor.options.headerStatusBarHeight
}
header={header({
layout: dimensions,
route: descriptor.route,
navigation: descriptor.navigation as BottomTabNavigationProp<ParamListBase>,
options: descriptor.options,
})}
style={sceneContainerStyle}
>
{descriptor.render()}
</Screen>
</BottomTabBarHeightContext.Provider>
</MaybeScreen>
);
})}
</MaybeScreenContainer>
<BottomTabBarHeightCallbackContext.Provider value={setTabBarHeight}>
{renderTabBar()}
</BottomTabBarHeightCallbackContext.Provider>
</SafeAreaProviderCompat>
</NavigationHelpersContext.Provider>
return (
<MaybeScreen
key={route.key}
style={StyleSheet.absoluteFill}
visible={isFocused}
enabled={detachInactiveScreens}
>
<BottomTabBarHeightContext.Provider value={tabBarHeight}>
<Screen
focused={isFocused}
route={descriptor.route}
navigation={descriptor.navigation}
headerShown={descriptor.options.headerShown}
headerStatusBarHeight={
descriptor.options.headerStatusBarHeight
}
header={header({
layout: dimensions,
route: descriptor.route,
navigation: descriptor.navigation as BottomTabNavigationProp<ParamListBase>,
options: descriptor.options,
})}
style={sceneContainerStyle}
>
{descriptor.render()}
</Screen>
</BottomTabBarHeightContext.Provider>
</MaybeScreen>
);
})}
</MaybeScreenContainer>
<BottomTabBarHeightCallbackContext.Provider value={setTabBarHeight}>
{renderTabBar()}
</BottomTabBarHeightCallbackContext.Provider>
</SafeAreaProviderCompat>
);
}

Expand Down
30 changes: 30 additions & 0 deletions packages/core/src/useComponent.tsx
@@ -0,0 +1,30 @@
import * as React from 'react';

export default function useComponent<
T extends React.ComponentType<any>,
P extends {}
>(Component: T, props: P) {
const propsRef = React.useRef<P | null>(props);

// Normally refs shouldn't be mutated in render
// But we return a component which will be rendered
// So it's just for immediate consumption
propsRef.current = props;

React.useEffect(() => {
propsRef.current = null;
});

return React.useRef((rest: Omit<React.ComponentProps<T>, keyof P>) => {
const props = propsRef.current;

if (props === null) {
throw new Error(
'The returned component must be rendered in the same render phase as the hook.'
);
}

// @ts-expect-error: the props should be fine here
return <Component {...props} {...rest} />;
}).current;
}
7 changes: 7 additions & 0 deletions packages/core/src/useNavigationBuilder.tsx
Expand Up @@ -14,6 +14,7 @@ import {
} from '@react-navigation/routers';
import NavigationStateContext from './NavigationStateContext';
import NavigationRouteContext from './NavigationRouteContext';
import NavigationHelpersContext from './NavigationHelpersContext';
import Group from './Group';
import Screen from './Screen';
import useEventEmitter from './useEventEmitter';
Expand All @@ -29,6 +30,7 @@ import useKeyedChildListeners from './useKeyedChildListeners';
import useOnGetState from './useOnGetState';
import useScheduleUpdate from './useScheduleUpdate';
import useCurrentRender from './useCurrentRender';
import useComponent from './useComponent';
import isArrayEqual from './isArrayEqual';
import {
DefaultNavigatorOptions,
Expand Down Expand Up @@ -586,9 +588,14 @@ export default function useNavigationBuilder<
descriptors,
});

const NavigationContent = useComponent(NavigationHelpersContext.Provider, {
value: navigation,
});

return {
state,
navigation,
descriptors,
NavigationContent,
};
}
21 changes: 14 additions & 7 deletions packages/drawer/src/navigators/createDrawerNavigator.tsx
Expand Up @@ -67,7 +67,12 @@ function DrawerNavigator({
);
}

const { state, descriptors, navigation } = useNavigationBuilder<
const {
state,
descriptors,
navigation,
NavigationContent,
} = useNavigationBuilder<
DrawerNavigationState<ParamListBase>,
DrawerRouterOptions,
DrawerActionHelpers<ParamListBase>,
Expand All @@ -83,12 +88,14 @@ function DrawerNavigator({
});

return (
<DrawerView
{...rest}
state={state}
descriptors={descriptors}
navigation={navigation}
/>
<NavigationContent>
<DrawerView
{...rest}
state={state}
descriptors={descriptors}
navigation={navigation}
/>
</NavigationContent>
);
}

Expand Down
13 changes: 5 additions & 8 deletions packages/drawer/src/views/DrawerView.tsx
Expand Up @@ -9,7 +9,6 @@ import {
import { useSafeAreaFrame } from 'react-native-safe-area-context';
import Animated from 'react-native-reanimated';
import {
NavigationHelpersContext,
DrawerNavigationState,
DrawerActions,
useTheme,
Expand Down Expand Up @@ -296,13 +295,11 @@ function DrawerViewBase({

export default function DrawerView({ navigation, ...rest }: Props) {
return (
<NavigationHelpersContext.Provider value={navigation}>
<SafeAreaProviderCompat>
<GestureHandlerWrapper style={styles.content}>
<DrawerViewBase navigation={navigation} {...rest} />
</GestureHandlerWrapper>
</SafeAreaProviderCompat>
</NavigationHelpersContext.Provider>
<SafeAreaProviderCompat>
<GestureHandlerWrapper style={styles.content}>
<DrawerViewBase navigation={navigation} {...rest} />
</GestureHandlerWrapper>
</SafeAreaProviderCompat>
);
}

Expand Down
Expand Up @@ -28,7 +28,12 @@ function MaterialBottomTabNavigator({
screenOptions,
...rest
}: Props) {
const { state, descriptors, navigation } = useNavigationBuilder<
const {
state,
descriptors,
navigation,
NavigationContent,
} = useNavigationBuilder<
TabNavigationState<ParamListBase>,
TabRouterOptions,
TabActionHelpers<ParamListBase>,
Expand All @@ -42,12 +47,14 @@ function MaterialBottomTabNavigator({
});

return (
<MaterialBottomTabView
{...rest}
state={state}
navigation={navigation}
descriptors={descriptors}
/>
<NavigationContent>
<MaterialBottomTabView
{...rest}
state={state}
navigation={navigation}
descriptors={descriptors}
/>
</NavigationContent>
);
}

Expand Down
Expand Up @@ -2,7 +2,6 @@ import * as React from 'react';
import { Text, StyleSheet, Platform } from 'react-native';
import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
import {
NavigationHelpersContext,
Route,
TabNavigationState,
TabActions,
Expand Down Expand Up @@ -75,7 +74,7 @@ try {
};
}

function MaterialBottomTabViewInner({
export default function MaterialBottomTabView({
state,
navigation,
descriptors,
Expand Down Expand Up @@ -192,14 +191,6 @@ function MaterialBottomTabViewInner({
);
}

export default function MaterialBottomTabView(props: Props) {
return (
<NavigationHelpersContext.Provider value={props.navigation}>
<MaterialBottomTabViewInner {...props} />
</NavigationHelpersContext.Provider>
);
}

const styles = StyleSheet.create({
icon: {
backgroundColor: 'transparent',
Expand Down

0 comments on commit 1179d56

Please sign in to comment.