React Navigation (Native): custom header - react-native

I'm having an issue with the react-navigation's drawer component not covering the whole application. I'm really struggling as I'm new with React Native and can't figure out the "clean way" to do it
Here's an example:
https://snack.expo.io/ZZqxmOQMw
When you press on "Toggle drawer" I expect it to cover the whole app, including the header, but it only covers the main content. On their examples, the drawer always only work with no content nor header.
Thank you!

I think this is a simple solution for you.
function withHeader(Component) {
return function(props) {
return (
<>
<Header />
<Component {...props} />
</>
)
}
}
function MyDrawer() {
return (
<Drawer.Navigator>
<Drawer.Screen name="Feed" component={withHeader(Feed)} />
<Drawer.Screen name="Notifications" component={withHeader(Notifications)} />
</Drawer.Navigator>
);
}
function Header() {
return <View style={{height: '50px', backgroundColor: 'red'}}>
<Text>My custom header!</Text>
</View>
}
export default function App() {
return (
<NavigationContainer>
<MyDrawer />
</NavigationContainer>
);
}

I think you should move the Header component to each screen or using the stack inside the drawer to create header

Related

How to use modalPresentationStyle .fullscreen in React Native navigation

I am trying to create my first React Native application. I have a login screen from which I want to navigate to a register screen if users want to sign up.
To achieve this, I was thinking of opening a modal above the first screen (login). I created the following:
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
const Stack = createStackNavigator();
function RegisterScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 30 }}>Register Screen</Text>
<Button onPress={() => navigation.goBack()} title="Go back" />
</View>
);
}
function MyStack() {
return (
<Stack.Navigator>
<Stack.Group>
<Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
</Stack.Group>
<Stack.Group screenOptions={{ presentation: "modal", headerShown: false }} options={{modalPresentationStyle: 'fullScreen'}}>
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Group>
</Stack.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<MyStack />
</NavigationContainer>
);
}
This works perfectly fine, the modal shows. However, as you can see in this screenshot below, the modal is shown with modalPresentationStyle .automatic (in iOS). I want it to show as .fullScreen, so the hierarchy is not visible in the screen. Basically, I want the view to be shown from the safeArea/statusBar all the way down to the safeArea on the bottom.
How can I achieve this?
The terminology in React Native isn't the same as terminology in native apps. If you want a full-screen screen, you probably don't want a modal. If you want a vertical animation, change the animation based on docs:
screenOptions={{ cardStyleInterpolator: CardStyleInterpolators. forVerticalIOS, headerShown: false }}
https://reactnavigation.org/docs/stack-navigator/#animation-related-options
There's also no modalPresentationStyle: 'fullScreen' in documentation and Group component doesn't take an options prop.
The accepted answer states:
If you want a full-screen screen, you probably don't want a modal.
That's not necessarily true. Use
screenOptions={{ presentation: 'transparentModal' }}
or
screenOptions={{ presentation: 'fullScreenModal' }}
for a full screen modal animation you can't dismiss on iOS.

Tab navigation lag when nested in drawer

I am having a issue when I am nesting my Tabs within my Drawer. Unfortunately, navigating to each tab is very slow, and their seems to be a lot of lag.
However, when I remove the Drawer navigator, and make it so that their is only a tab navigator, navigating between the different tab screens is noticeably better.
How can I make it so that their is no delay between the tabs when the tabs are nested in to the drawer?
{ *
With help from Mateusz, I have managed to pinpoint the issue. I tested the delay by rendering four of the same components. The first test was using
children={() => {
return <NfcWifiConfig />;
}}
And the delay was still there
But then, when I used
component={NfcWifiConfig}
The delay is completely gone and navigation is running smoothly as it should. So my question now is, where do i go from here? How would i pass the props down with this syntax?
}
My current code is:
const DrawerComponent = ({
Bunch of props here
}) => {
return (
<Drawer.Navigator
drawerType="back"
drawerContent={(props) => {
return (
<DrawerContent
{...props}
/>
);
}}
>
{/* TABS */}
<Drawer.Screen
name="MainHome"
children={({navigation}) => {
return (
<>
<StatusBar backgroundColor={homeColor} barStyle="dark-content" />
<Navbar navigation={navigation} userimage={userimage} />
<Tabs.Navigator>
{/* HOME STACK */}
<Tabs.Screen
name="Profile"
children={() => {
return (
<>
<MainStackNavigator
{Bunch of props here}
/>
</>
;
}}
/>
{/* SEARCH SCREEN */}
<Tabs.Screen
name="Search"
children={() => {
return (
<>
<StatusBar barStyle="dark-content" />
<SearchStack
{ Bunch of props here }
/>
</>
);
}}
/>
{/* NFC-SOCIAL SCREEN */}
<Tabs.Screen name="Activate" component={NfcConfig} />
{/* NFC-WIFI SCREEN */}
<Tabs.Screen name="WiFi" component={NfcWifiConfig} />
</Tabs.Navigator>
</>
);
}}
/>
{/* Add Links Screen */}
<Drawer.Screen
name="Add Links"
children={({navigation}) => {
return (
<AddLinksScreen
{ Bunch of props here }
/>
);
}}
/>
{/* Following Screen */}
<Drawer.Screen
name="Followers"
children={({navigation}) => {
return (
<FollowerStack
{ Bunch of props here }
/>
);
}}
/>
{/* Following Screen */}
<Drawer.Screen
name="Following"
children={({navigation}) => {
return (
<FollowingStack
{ Bunch of props here }
/>
);
}}
/>
</Drawer.Navigator>
);
};
Also, the add links screen and followers/following screens work fine. Navigating to them works efficiently with no lag. But the tabs => home stack, search screen and the other two, have a heavy delay when navigating between them.
In terms of the content inside the tabs, the last two tabs are very light, and do not contain much content. I have tried commenting out the heavy tab screens and using just the two lightweight components, but same result. Making me believe that is not the issue.
So I managed to fix the issue. When I used:
children={() => {
return <NfcWifiConfig props{props} />;
}}
The lag was present. However, when I used:
component={NfcWifiConfig}
The lag disappeared. However, my props were not being passed through. So what I did was use React Context to pass my props to all the different components that needed it and that's it, the lag was gone and the components were receiving the props as I wanted.
Also, the code is a lot cleaner when using React context, so I highly recommend it.

Adding background color to app.js on react-native

I want to add a default color for all my screens on react-native. My entry file doesn't have a default react-native component that takes in styles as prop. Hence, I'll be asking "How to add styles when Navigation container is present". I have tried adding the cardStyle prop on Drawer.Screen but it doesn't work.
Here's a sample of my code
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name='SignUp' component={SignUp} />
<Drawer.Screen name='Reset Password' component={ResetPassword}/>
<Drawer.Screen name="Forgot Password" component={ForgotPasswordScreen} />
<Drawer.Screen name='Login' component={Login} />
<Drawer.Screen name="Verify Email" component={EmailVerification} />
</Drawer.Navigator>
</NavigationContainer>
Simply we have to create a Root Component in which we can put the background color.
For Example.
Create a Root Component with background color:
const RootComponent = props => {
return (
<View style={{ flex: 1, backgroundColor: "YOUR_BACKGROUND_COLOR" }} >
{props.children}
</View>
)
}
export default RootComponent;
Now we can import the Root Component in our screens in which screens we are supposed to show the same background color.
for Example:
const HomeScreen = props => {
return (
<RootComponent>
{
/* YOUR HOME SCREEN COMPONENTS... */
}
</RootComponent>
)
}

Adding a back button for each bottom tab with React Navigation

The bottom tabs navigation is looking something like that:
const Tab = createBottomTabNavigator();
export default function TabStackScreen() {
return (
<Tab.Navigator initialRouteName="Home">
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Favorites" component={FavoritesStackScreen} />
<Tab.Screen name="Search" component={SearchStackScreen} />
</Tab.Navigator>
)
}
There is not back button on the favorites and search screen. I guess this is the normal behavior, but I would love to have one.
I did not find something relevant in the doc, except recreating a component that looks like the native back button and adding it on some screens using headerLeft. Is there a more simple method?
In my projects I like to create custom headers, and I do not show the default header and show my own custom header component.
On my custom component I add right and left components, and let the screens decide what to do, by default the back button is shown, but if the screen pass the noBack property, the button is hidden.
You can also add a right component, for example a close button.
That is what I use to do:
const screenOptions = {
headerShown: false
};
<RootStack.Navigator screenOptions={screenOptions} mode="modal">
and then create my own component
export const Header = ({ title, leftComponent, rightComponent, noBack }) => {
const navigation = useNavigation();
return (
<Wrapper>
{leftComponent ||
(noBack ? (
<Placeholder />
) : (
<Button
onPress={() => navigation.goBack()}
accessible
accessibilityRole="button"
accessibilityLabel="Back">
<Icon
size={30}
name={Platform.OS === 'android' ? 'arrow-left' : 'chevron-left'}
/>
</Button>
))}
<Title bold accessibilityRole="header" acessible acessibilityText={title}>
{title}
</Title>
{rightComponent || <Placeholder />}
</Wrapper>
);
};
Doing that, you are able to customize everything on your header, it works like a charm for me.

How do I call navigator from outside the Navigator?

I have a tab bar in my application that I don't want the navigation transition to affect each time a new route is rendered. Therefore I want to place the tab bar outside the Navigator, but how do I trigger the navigation actions in that case? I cannot access the navigator object that is passed to the renderScene function from outside the Navigator.
Here is what is returned in app.js:
<View>
<Navigator
ref="navigator"
initialRoute={{name: "start"}}
renderScene={this.renderScene.bind(this)}
configureScene={this.configureScene.bind(this)}
/>
<TabBar navigator={???} style={styles.nav} />
</View>
Ok, I think I solved this. The problem seems to be that the refs is not available at the first render of the component. I solved this by:
<Navigator
ref={(r) => { this.navigatorRef = r; }}
initialRoute={{name: "start"}}
renderScene={this.renderScene.bind(this)}
configureScene={this.configureScene.bind(this)}
/>
{this.state ? <TabBar navigator={this.state.navigatorRef} style={styles.nav} /> : null }
and added the this:
componentDidMount() {
this.setState({navigatorRef: this.navigatorRef});
}
just to make the component render a second time with the TabBar.
I don't think this is the best way to do what you want. But to answer your question:
<View>
<Navigator
ref="navigator"
initialRoute={{name: "start"}}
renderScene={this.renderScene.bind(this)}
configureScene={this.configureScene.bind(this)}
/>
<TabBar getNavigator={()=>this.refs.navigator} style={styles.nav} />
</View>
And then you could call getNavigator() from your TabBar
Here's another version that worked for me. It is hackish and will probably break in the future but might help if you're in a hurry ;)
<View>
<Navigation
ref={(r) => { this.navigation = r }}
/>
<OtherScreen
navigation={(this.navigation || {})._navigation}
{...this.props}
/>
</View>