Passing props to Custom Drawer Navigator in React Navigation - javascript

In react navigation drawermenu. I want to display the username 'John Doe' which is in the state of my main component 'Router' How can I pass it the CustomDrawerContentComponent.
Extra Info: I'm getting this name from AsyncStorage in
componentDidMount
This is my code
export default class Router extends Component {
constructor(props) {
super(props);
this.state = {
employeeName: "John Doe" //<-----How to pass this name to CustomDrawerContentComponent????
};
}
render() {
return <MyApp />;
}
}
const MyApp = DrawerNavigator(
{
Home: {
screen: Home
},
History: {
screen: History
},
AddDetails: {
screen: AddDetails
}
},
{
initialRouteName: "Home",
drawerPosition: "left",
contentComponent: CustomDrawerContentComponent,
drawerOpenRoute: "DrawerOpen",
drawerCloseRoute: "DrawerClose",
drawerToggleRoute: "DrawerToggle",
contentOptions: {
activeTintColor: "#EF4036"
}
}
);
const CustomDrawerContentComponent = props => (
<Container>
<Header style={styles.drawerHeader}>
<Body style={styles.container}>
<Image
style={styles.drawerImage}
source={{
uri: "http://themes.themewaves.com/nuzi/wp-content/uploads/sites/4/2013/05/Team-Member-3.jpg"
}}
/>
<Text style={styles.drawerText}>**How get the name here?**</Text>
</Body>
</Header>
<Content>
<DrawerItems {...props} />
</Content>
</Container>
);

You can use screenProps:
<MyApp
screenProps={{employeeName: this.state.employeeName}}
/>
And then use it in your custom contentComponent:
contentComponent:(props)=>(
<View>
<Text>{props.screenProps.employeeName}</Text>
<DrawerItems {...props} />
</View>
)
This is the answer for your question. But first of all I don't recommend using your top level component for such business logic. Use it only for navigation.
And see my answer here: Customizing Drawer
I recommend you to create a Redux connected custom drawer as I describe in my answer. If you don't know how to use Redux, I definitely recommend you to learn it. You will need it as your application grows.

Just pass as screenprops
Render(){
return <Myapp screenprops={employeename:this.state.employeeName}/>;
}
You can access it from drawer like this this.props.screenprops.employeename

Related

React Native Stack Navigation Params

I'm trying to use params on the header of my Image details screen!
Here's a brief explanation.
My user enters an input, I call an api and display the info on the screen:
Home.js
<View style={styles.viewpic}>
<TouchableOpacity onPress={() => navigation.navigate('ImageDetails',
item)}>
<Image
style={{
height: 104,
}}
source={{uri:item.url}}/>
</TouchableOpacity>
</View>
Then, the user clicks on the chosen data, displayed on the screen, and my app navigates to the details page:
ImageDetails.js
export default function ImageDetails({navigation}) {
return(
<ScrollView>
<View>
<Image
source={{uri:navigation.getParam('url')}}/>
<View style={styles.descriptionBox}>
<Text style={styles.imageDet}>Description:{" "}
{navigation.getParam('explanation')}</Text>
</View>
</View>
</ScrollView>
this is the navigation folder I have:
homeStack.js
const screens = {
Home: {
screen: Home,
navigationOptions:{ headerShown: false}
},
ImageDetails: {
screen: ImageDetails,
navigationOptions: () => {
return{
headerTitle: () => <Header/>,
}
}
}
}
const HomeStack = createStackNavigator(screens);
export default createAppContainer(HomeStack);
plus the HEADER component that I'm trying to use in the header navigation (top of the screen):
Header.js
export default function Header({navigation}) {
return(
<View style={styles.descriptionBox}>
<Text style={styles.imageDet}>Params here!</Text>
</View>
)
Here is what the Image detail screen looks like:
My goal is:
to be able to use the data params on the header.
I tried a few different combos but I keep on getting the error: "cant read params"
Some of the things I tried:
Header.js :
export default function Header({navigation}) {
return(
<View style={styles.descriptionBox}>
<Text style={styles.imageDet}>Test:{navigation.getParam('item')}
</Text>
</View>
)
Homestack component:
homeStack.js
const screens = {
Home: {
screen: Home,
navigationOptions:{ headerShown: false}
},
ImageDetails: {
screen: ImageDetails,
navigationOptions: ({navigation}) => {
return{
headerTitle: () => <Header navigation=
{navigation.getParams('title')}/>,
}
}
}
}
const HomeStack = createStackNavigator(screens);
export default createAppContainer(HomeStack);
I also have read the documentation but I'm not sure how I would insert the "Navigation.push" with params here.
Thanks for your help!
try using the existent image details page instead of creating
a new one!
ImageDetails: {
screen: ImageDetails,
navigationOptions: ({navigation}) => {
return{
headerTitle: () => {
return(
<View>
<Text>{navigation.getParam('title')}
</Text>
</View>
)
},

Resetting screen to first Parent screen, from a nested screen (React navigation & React Native)

I've followed the documentation for creating bottom tab navigation with react-navigation v5 ("#react-navigation/native": "^5.2.3")
Currently is partially used this example in my project from docs https://reactnavigation.org/docs/bottom-tab-navigator/ to fit the needs of version 5.
Example might be following
// Navigation.tsx
import { BottomTabBarProps } from '#react-navigation/bottom-tabs';
import { TabActions } from '#react-navigation/native';
import * as React from 'react';
function Navigation({ state, descriptors, navigation }: BottomTabBarProps) {
return (
<View>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
const jumpToAction = TabActions.jumpTo(options.title || 'Home');
navigation.dispatch(jumpToAction);
}
};
return (
<TouchableOpacity
key={options.title}
accessibilityLabel={options.tabBarAccessibilityLabel}
accessibilityRole="button"
active={isFocused}
activeOpacity={1}
testID={options.tabBarTestID}
onPress={onPress}
>
{route.name}
</TouchableOpacity>
);
})}
</View>
);
}
export default Navigation;
However, I have a couple of nested StackNavigators as described in AppNavigator.tsx
AppNavigator.tsx
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import React from 'react';
import { AppState, AppStateStatus } from 'react-native';
import Navigation from '../components/navigation/Navigation';
import AccountScreen from '../screens/account';
import SettingsScreen from '../screens/settings';
import SupportScreen from '../screens/support';
import HomeNavigator from './HomeNavigator';
import TransactionNavigator from './TransactionNavigator';
const { Navigator, Screen } = createBottomTabNavigator();
const AppNavigator = () => {
return (
<View>
<Navigator tabBar={(props) => <Navigation {...props} />}>
<Screen
component={HomeNavigator}
name="Home"
options={{ title: 'Home' }}
/>
<Screen
component={TransactionNavigator}
name="Transactions"
options={{
title: 'Transactions' }}
/>
<Screen
component={AccountScreen}
name="Account"
options={{ title: 'Account' }}
/>
<Screen
component={SupportScreen}
name="Support"
options={{ title: 'Support' }}
/>
<Screen
component={SettingsScreen}
name="Settings"
options={{
title: 'Settings' }}
/>
</Navigator>
</View>
);
};
export default AppNavigator;
And I am aiming for resetting the nested StackNavigator each time user leaves it. So example can be HOME -> TRANSACTIONS -> TRANSACTION_DETAIL (which is part of a nested navigator) -> HOME -> TRANSACTIONS
currently, I see a TRANSACTION_DETAIL after the last step of the "walk through" path. Nevertheless, I want to see TRANSACTIONS instead. I found that if I change
if (!isFocused && !event.defaultPrevented) {
const jumpToAction = TabActions.jumpTo(options.title || 'Home');
navigation.dispatch(jumpToAction);
}
to
if (!isFocused && !event.defaultPrevented) {
navigation.reset({ index, routes: [{ name: route.name }] });
}
it more or less does the thing. But it resets the navigation, so it is unmounted and on return back, all data are lost and need to refetch.
In navigation is PopToTop() function that is not available in this scope.
Also I tried to access all nested navigators through descriptors, yet I have not found how to correctly force them to popToTop.
And the idea is do it on one place so it will be handled automatically and there would not be any need to implement it on each screen.
I have tried with navigator.popToTop() but it was not working. It may be stackNavigator and TabNavigator having a different history with the routes. I have fixed the issue with the below code. "Home" is my stack navigator name and another "Home" is screen name (Both are same for me)
tabBarButton: props => (
<TouchableOpacity
{...props}
onPress={props => {
navigation.navigate('Home', {
screen: 'Home'
})
}}
/>
),

Trying to inject a stack navigator into a component and getting error stating "you should only render one navigator"

I have a case where a feature contains a FlatList full of information, a search bar, sort button, and filter button.
For the sort and filter buttons I need to pull up a modal from the bottom that takes up half the screen.
I understand that React Navigation wants us to only create one 'root' navigator and all other navigators be dependents; however, in this particular case I would very much like to explicitly add a navigator to this page where a user presses on the filter button, brings up the modal, presses a filter option and then have the modal navigate to another filter subpage within the confines of its view, while maintaining the main page content and root navigation state in the background.
I remember implementing this in React Navigation V1.x, but does anyone know how to get around this in V2.x?
Rather than doing it with nested stack navigator and things, I've implemented your requirement using built-in react native modal.
App
import React, { Component } from 'react';
import { createStackNavigator } from 'react-navigation';
import { MainScreen } from './src/screens/MainScreen';
const RootStack = createStackNavigator(
{
MainScreen
},
{
navigationOptions: {
header: null
}
}
);
export default class App extends Component {
render() {
return (
<RootStack />
);
}
}
MainScreen
import { default as React, PureComponent } from 'react';
import { View, Text, Button, Alert, Modal } from 'react-native';
interface Props {
}
interface States {
num: number;
isFilterOneVisible: boolean;
isFilterTwoVisible: boolean;
}
export class MainScreen extends PureComponent<Props, States> {
state = {
num: 0,
isFilterOneVisible: false,
isFilterTwoVisible: false
}
render() {
return (
<View
flex={1}
justifyContent={'space-evenly'}
alignItems={'center'}
>
<Text style={{ fontSize: 50 }}>{this.state.num}</Text>
<Button
title={'CHANGE STATE'}
onPress={() => {
this.setState((prevState: States) => ({
num: prevState.num + 1
}));
}}
/>
{/* Search */}
<Button
title={'Search'}
onPress={() => {
Alert.alert('Search', 'Search clicked');
}}
/>
{/* Sort*/}
<Button
title={'Sort'}
onPress={() => {
Alert.alert('Sort Clicked', 'Sort clicked')
}}
/>
<Button
title={'Filter'}
onPress={() => {
this.setState({
isFilterOneVisible: true
})
}}
/>
{/* Filter Modal 1*/}
<Modal
visible={this.state.isFilterOneVisible}
transparent={true}
animationType={'slide'}
onRequestClose={() => {
this.setState({
isFilterOneVisible: false
})
}}
>
<View
flex={1}
justifyContent={'flex-end'}
backgroundColor={'rgba(0,0,0,0.2)'}
>
{/* Bottom */}
<View
justifyContent={'center'}
alignItems={'center'}
backgroundColor={'white'}
height={200}
>
<Button
title={'GO TO NEXT FILTER STATE'}
onPress={() => {
this.setState({
isFilterOneVisible: false,
isFilterTwoVisible: true
})
}}
/>
</View>
</View>
</Modal>
{/* Filter Modal Two */}
<Modal
visible={this.state.isFilterTwoVisible}
transparent={true}
animationType={'slide'}
onRequestClose={() => {
this.setState({
isFilterTwoVisible: false
})
}}
>
<View
flex={1}
justifyContent={'flex-end'}
backgroundColor={'rgba(0,0,0,0.2)'}
>
{/* Bottom */}
<View
justifyContent={'center'}
alignItems={'center'}
backgroundColor={'white'}
height={200}
>
<Button
title={'SET DATA AS 1000'}
onPress={() => {
this.setState({
isFilterTwoVisible: false,
num: 1000
})
}}
/>
</View>
</View>
</Modal>
</ View >
);
}
}
NOTE: The code is not optimised and follows some bad patterns like arrow-methods-in-jsx. This is just a suggestion with a working example. Feel free to enhance the code and follow the divide-and-conquer strategy ;) . The full source code can be found from here

Passing a component a navigation screen via props from screen component

I am building an app that has a list on the home screen routing to a number of other screens.
I created a list component that is rendered on the home page, and therefore, need to pass the navigation down to the list component. The list component, in turn, will determine which screen to display depending on which item is pressed.
I am using a Stack Navigator on my router.js file
export const HomeStack = StackNavigator({
Home: {
screen: Home,
navigationOptions: {
title: 'Home',
},
},
Nutrition: {
screen: Nutrition,
navigationOptions: {
title: 'Nutrition',
}
},
});
In my home.js screen I have the below code inside the render method
render() {
return (
<View>
<View>
<ListComponent navigate={this.props.navigation.navigate('')} />
<Button
title="Go to Nutrition"
onPress={() => this.props.navigation.navigate('Nutrition')}
/>
</View>
</View>
);
}
The Button successfully routes to the Nutrition.js screen.
But, I try to get my ListComponent.js to handle where to route as this file maps through my list array.
render() {
return (
<List>
{ListData.map((listItem, i) => (
<ListItem
key={i}
title={listItem.title}
leftIcon={{ name: listItem.icon }}
onPress={this.onPressHandler(listItem.title)}
/>
))}
</List>
);
}
How can I properly pass the navigation as props down to ListComponent.js and then use the title from the list array to determine which screen to display?
change this line :
<ListComponent navigate={this.props.navigation.navigate('')} />
to this :
<ListComponent navigate={(screen)=>this.props.navigation.navigate(screen)}/>
and change this
<ListItem
key={i}
title={listItem.title}
leftIcon={{ name: listItem.icon }}
onPress={this.onPressHandler(listItem.title)}
/>
to this :-
<ListItem
key={i}
title={listItem.title}
leftIcon={{ name: listItem.icon }}
onPress={()=>this.props.navigate(listItem.title)}
/>
As you are calling the method directly not binding it to the component.
I am assuming that your code in ListItem.js is correct.

Is there any chance to use a component as a global ActivityIndicator on React-Native

Is there any chance to use a component as a global ActivityIndicator which has transparent color and had been created by me on React-Native?
Details:
I use a redux store to update the UI. So I intend to show an ActivityIndicator by updating the store.
I've created an ActivityIndicator component with name ActIndicator.
I have a main App component that contains the app.
I have a Root component that wraps the ActIndicator and App components.
The ultimate code of render method of Root component looks like the following:
render() {
if (this.state.showActivityIndicator) {
return(
<ActIndicator>
<App />
</ActIndicator>
)
}
return (</App>)
}
I've tried several methods but I can not be successful.
I guess my brain is stopped.
I also guess there may be a logic mistake.
const withLoadingOverlay = (Component) => class withLoadingOverlayView extends React.Component { props: withLoadingOverlayProps
// Indicator view styles loadingOverlay = (style) => (
<View style={[style, styles.someDefaultStyles]}>
<ActivityIndicator color={someColor} size="large" />
</View> )
render() {
const { pending, ...passProps } = this.props;
const { width, height } = Dimensions.get('window');
return (
<View style={{ flex: 1 }}>
<Component {...passProps} />
{pending && this.loadingOverlay({ width, height })}
</View>
); } };
I used to wrap whole container with HOC and with redux action to set on start pending prop true and on success or fail to set on false so this prop will be consumed by HOC and indicator will be displayed only when pending is set on true.
In container you have to wrap component in connect
export default connect(
(state) => ({
pending: state.reducer.pending, // pending prop should be here
}),
(dispatch) => ({ dispatching redux actions })
)(withLoadingOverlay(WrappedComponent));
I don't think you're supposed to pass App as a child, the way I use it is more like this:
render() {
if (this.state.showActivityIndicator) {
return(
<View>
<ActivityIndicator />
<App />
</View>
)
}
return (<App />)
}
but it would probably be better to set it up like this:
render() {
return (
<View>
<ActivityIndicator animating={this.state.showActivityIndicator} />
<App />
</View>
)
}

Categories

Resources