react naviagation v5 navigate from drawer custom view - react-native

I am using React Navigation V5, I wanna custom drawer Navigation content which contents the image on top and some others navigation items unders
Here is my drawer items:
Image (custom view)
Profile
Products
Orders
Here is my code my custom drawer content.
export const CustomDrawerContent = props => {
return (
<SafeAreaView style={styles.customDrawer}>
<View
style={{ flex: 1 }}
>
<DrawerContentScrollView {...props}>
<TouchableNativeFeedback onPress={() => { console.log('go profile'); }}>
<View style={styles.userContainer}>
<View style={styles.imageContainer}>
<Image
style={styles.image}
source={{ uri: 'https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTLCta_MQcJFd2kpz8HwXFm-6vxVqXzRUgCOIuhs94Q32GG8EeJ' }}
/>
</View>
<Text style={styles.name}>Nguyen van Admin</Text>
</View>
</TouchableNativeFeedback>
<DrawerItemList {...props} />
</DrawerContentScrollView>
<DrawerItem
label="Đăng xuất"
style={{
borderWidth: 1,
}}
labelStyle={{
color: 'black'
}}
icon={({ focused, color, size }) => <Ionicons
size={23}
color={color}
name={Platform.OS === 'android' ? 'md-exit' : 'ios-exit-outline'}
/>}
/>
</View>
</SafeAreaView>
);
}
So If the profile screen existed in drawer, By clicking to the image i can use
props.navigate("profile")
But if I remove the profile screen from the drawer screens. I can not navigate to profile anymore.
How do i archive navigate to profile screen without adding it the drawer screens?
Or Can I hide profile item from drawer items?

To hide a menu item from drawer, use Array.map(...) instead of <DrawerItemList {...props} /> in custom drawer content.,
{drawerItems.map((item, index) => {
return (
<DrawerItem
label={item.drawerLabel}
onPress={() => props.navigation.navigate(item.routeName)}
/>
);
})}
and add a useEffect hook to custom drawer content like below,
let [drawerItems, setDrawerItems] = useState([]);
useEffect(() => {
let drawerItemsList = [];
for (const key in props.descriptors) {
if (props.descriptors.hasOwnProperty(key)) {
if (!key.includes('profile')) {
const element = props.descriptors[key];
element.options.routeName = key.substring(0, key.indexOf('-'));
drawerItemsList.push(element.options);
}
}
}
setDrawerItems(drawerItemsList);
}, []);
Another approach.,
Create an Array like below in the custom drawer content.,
const drawerItemsList = [
{
drawerLabel: 'Products',
drawerIcon: 'product',
routeName: 'products',
active: true,
},
{
drawerLabel: 'Orders',
drawerIcon: 'order',
routeName: 'orders',
active: false,
},
];
let [drawerItems, setDrawerItems] = useState(drawerItemsList);
and instead of <DrawerItemList {...props} /> use <Flatlist /> like below.,
<View>
<FlatList
data={drawerItems}
keyExtractor={(item)=>item.routeName.trim()}
renderItem={({item,index})=>(
<DrawerItem
label={item.drawerLabel}
icon={({color, size}) => <Ionicons name={item.drawerIcon} color={item.active?'#1e90ff':'#ccc'} size={size} />}
labelStyle={[item.active?{color: '#1e90ff'}:{color: '#ccc'}]}
style={item.active?{backgroundColor: '#1e90ff20'}:null}
onPress={() => {
drawerItemsList.forEach((element,i) => {
i!==index?element.active=false:element.active=true
});
setDrawerItems(drawerItemsList)
props.navigation.navigate(item.routeName)
}}
/>
)}
/>
</View>

Related

I am not able to navigate to the other screen in my react native project the error is: navigation not found

const Item = ({ item }) => (
<TouchableOpacity
onPress={() => this.navigation.navigate(
"carProfile",
{item: item}
)}>
<View style={styles.item}>
<Text style={styles.title}>{item.name}</Text>
<Text style={styles.details}>{item.details}</Text>
</View>
</TouchableOpacity>
);
const List = (props,navigation) => {
const renderItem = ({ item }) => {
if (props.searchPhrase === "") {
return <Item item={item} />;
}
if (item.name.toUpperCase().includes(props.searchPhrase.toUpperCase().trim().replace(/\s/g, ""))) {
return <Item item={item} />;
}
if (item.details.toUpperCase().includes(props.searchPhrase.toUpperCase().trim().replace(/\s/g, ""))) {
return <Item item={item} />;
}
};
return (
<SafeAreaView style={styles.list__container}>
<View
onStartShouldSetResponder={() => {
props.setClicked(false);
}}
>
<FlatList
data={props.data}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
</View>
</SafeAreaView>
);
};
export default List;
the error is continuing to be there despite me trying to put navigation container in different positions. I want to send the user to the carProfile page, where the data passed in item is reused. This way user can know about the selection they are looking for
Use this way
onPress={() => this.props.navigation.navigate(
"carProfile",
{item: item}
)}
The navigation is inside the props, not as a second argument. Below is a classic example. And also note, you cannot extract navigation prop for child components, it will only work on Main components(Screens).
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
If you want to use navigation inside child components, you can use the navigation hooks.
import { useNavigation } from "#react-navigation/native";
function MyBackButton() {
const navigation = useNavigation();
return (
<Button
title="Back"
onPress={() => {
navigation.goBack();
}}
/>
);
}

React-Native modal not showing FlatList Items

Modal only displays button but not FlatList item.
I am trying to build a custom app picker for my react-native-app which renders a list of items on a modal.
AppPicker.js
const AppPicker = ({ icon, items, placeholder }) => {
const [modalVisible, setModalVisible] = React.useState(false)
return (
<>
<TouchableWithoutFeedback onPress={() => setModalVisible(true)} >
<View style={styles.container}>
{icon && <MaterialCommunityIcons name={icon} size={20} style={styles.icon} />}
<AppText style={styles.text}> {placeholder} </AppText>
<MaterialCommunityIcons name="chevron-down" size={20} />
</View>
</TouchableWithoutFeedback>
<Modal visible={modalVisible} animationType="slide" >
<Button title="Close" onPress={() => setModalVisible(false)} />
<FlatList
data={items}
keyExtractor={item => item.value.toString()}
renderItem={({ item }) =>
<PickerItem
lable={item.label}
onPress={() => console.log(item)}
/>}
/>
</Modal>
</>
)
}
the <PickerItem/> that should be rendered by the flatlist is a component that just displays the items from the list.
PickerItem.js
const PickerItem = ({ label, onPress }) => {
return (
<TouchableOpacity onPress={onPress}>
<AppText> {label} </AppText>
</TouchableOpacity>
)
}
export default PickerItem;
when I randomly tap on the modal screen however the onPress() on the FlatList triggers the console.log(item) and the items are displayed on the console. Any help on this will be appreciated.
** App.js **
const categories = [
{ label: "furniture", value: 1 },
{ label: "clothing", value: 2 },
{ label: "phone", value: 3 },
]
export default function App() {
return (
<Screen style={styles.container}>
<AppPicker items={categories} icon="apps" placeholder="category" />
<AppTextInput icon="email" placeholder="email" />
</Screen>
);
}
NOTE: I am using expo to run the application.

Trigger an onPress Function from anonther component

I want to trigger an onPress function from the search icon in the navbar.
This is the search component:
function SearchIcon(props) {
const theme = useSelector(state => state.themer.theme);
return (
<Icon.Button
name="search"
size={22}
color={theme.icons}
backgroundColor={theme.top_tab}
onPress={() => {}}
/>
);
}
export default SearchIcon;
The search component is being called in the specific stack, where it's needed.
<Stack.Screen
name="Home"
component={Home}
options={({navigation, route}) => ({
...,
headerRight: props => (
<View style={{flexDirection: 'row'}}>
<SearchIcon />
<CartIcon navigation={navigation} />
</View>
),
})}
/>
On the home screen, I have an isSeacrhing constant that should change value from false to true and vice versa.
const [data, setData] = React.useState({
isSearching: false,
search: '',
...
});
// TRIGGERED BY SEARCH ICON IN NAV BAR
const toggleSearch = () => setData({...data, isSearching: !isSearching});
{data.isSearching == false ? (
<ScrollView
...
</ScrollView>
) : (
<View>
<TextInput
style={[styles.textInput, [{color: theme.text, ...FONTS.body4}]]}
value={data.search}
placeholder="Search..."
placeholderTextColor={theme.text}
onChangeText={()=>{}}
/>
</View>
)}
Is it possible to trigger the onPress function or is there another way I can make it work? The search icon is on two screen, does calling the same function make the TextInput appear on both?
Wherever you use <SearchIcon /> just add a prop in that like this
<SearchIcon onPress={() => { // Do Something }} />
Then in your SearchIcon
function SearchIcon(props) {
const theme = useSelector(state => state.themer.theme);
return (
<Icon.Button
name="search"
size={22}
color={theme.icons}
backgroundColor={theme.top_tab}
onPress={props.onPress} // Access it here like this
/>
);
}
export default SearchIcon;

React Native functional component navigationOptions headerRight not being set

Previously I have set the headerRight option in the root component that renders child component with Screen options like this
export default function App() {
return (
<RecipeProvider>
<NavigationContainer>
<Stack.Navigator initialRouteName="Recipes">
<Stack.Screen
name="Recipes"
component={RecipeList}
options={({ navigation }) => ({
headerRight: () => (
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate("New Recipe")}
>
<FontAwesomeIcon icon={faPlus} size={20} />
</TouchableOpacity>
),
})}
/>
and now I would like to move the headerRight inside the component definition (so I don't end up with a huge App file with details that are only relevant to the screen component themselves)
I have read other solutions and tried the following
export default function RecipeList({ navigation }) {
const { recipes } = useContext(RecipeContext);
return (
<View style={styles.container}>
<FlatList
numColumns={2}
data={recipes}
keyExtractor={(recipe: Recipe) => recipe.id}
renderItem={({ item }) => {
return (
<RecipeItem
name={item.name}
minutes={item.minutes}
image={item.image}
title="Go to Detail Screen"
onPress={() => {
navigation.navigate("Recipe Details", { item });
}}
/>
);
}}
/>
</View>
);
}
RecipeList.navigationOptions = ({ navigation }) => ({
headerRight: () => (
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate("New Recipe")}
>
<FontAwesomeIcon icon={faPlus} size={20} />
</TouchableOpacity>
),
});
But the headerRight button doesn't show no more ... any ideas ?
Thank you all <3
My headerRight is below and it works for me
EditScreen.navigationOptions = navData => {
const submitFn = navData.navigation.getParam('submit');
return {
headerTitle: 'Edit Data'
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Save"
iconName={
Platform.OS === 'android' ? 'md-checkmark' : 'ios-checkmark'
}
onPress={submitFn}
/>
</HeaderButtons>
)
};
};

Logout using react Native DrawerNavigator

I am building a react-native app(App Image)which has Logout link on the Drawer Navigator.
Code is as below
const DrawerScreen = DrawerNavigator({
..........
logout: {
screen: Component
},
})
export default DrawerScreen;
But the problem is , it's only loading the component screen. i need to call a method where i can perform Asyncstorage clear and navigate to LoginPage.
You probably want to add a button to your drawer.
If so, your code will look like this.
const Drawer = DrawerNavigator(
{
mainpage:{screen:MyScreen}
},
{
contentComponent:(props) => (
<View style={{flex:1}}>
<SafeAreaView forceInset={{ top: 'always', horizontal: 'never' }}>
<DrawerItems {...props} />
<Button title="Logout" onPress={DO_SOMETHING_HERE}/>
</SafeAreaView>
</View>
),
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle'
})
You should import import {DrawerItems} from 'react-navigation'; to get it work.
UPDATE:
In 4.x version of react-navigation you should import `import {DrawerNavigatorItems} from 'react-navigation-drawer'
You should import SafeAreaView from 'react-native-safe-area-view'
const DrawerNavigation = createDrawerNavigator(
{
Mainpage: {
screen: Mainpage
}
},
{
contentComponent:(props) => (
<View style={{flex:1}}>
<SafeAreaView forceInset={{ top: 'always', horizontal: 'never' }}>
<DrawerItems {...props} />
<TouchableOpacity onPress={()=>
Alert.alert(
'Log out',
'Do you want to logout?',
[
{text: 'Cancel', onPress: () => {return null}},
{text: 'Confirm', onPress: () => {
AsyncStorage.clear();
props.navigation.navigate('Login')
}},
],
{ cancelable: false }
)
}>
<Text style={{margin: 16,fontWeight: 'bold',color: colors.textColor}}>Logout</Text>
</TouchableOpacity>
</SafeAreaView>
</View>
),
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle'
}
);
You can use Component class for perform Asyncstorage clear and navigate Login Page. If you use react-navigation this.props has navigation object.
class YourComponent extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
Asyncstorage.clear();
this.props.navigation.navigate('LoginPage')
}
}
export default YourComponent;
import {StyleSheet,AsyncStorage,Alert, View,SafeAreaView, Text, ActivityIndicator, Dimensions, TextInput,Image, TouchableOpacity, TouchableHighlight} from 'react-native';
import {DrawerItems,DrawerActions} from 'react-navigation-drawer';
{
contentComponent:(props) => (
<View style={{flex:1}}>
<SideMenu {...props}/>
<DrawerItems {...props} />
<TouchableOpacity onPress={()=>
Alert.alert(
'Log out',
'Do you want to logout?',
[
{text: 'Cancel', onPress: () => {this.props.navigation.dispatch(DrawerActions.closeDrawer()) }},
{text: 'Confirm', onPress: () => {
AsyncStorage.clear();
props.navigation.navigate('LoginScreen')
}},
],
{ cancelable: false }
)
}>
<Text style={{margin: 16,fontWeight: 'bold',color: 'red'}}>Logout</Text>
</TouchableOpacity>
</View>
),
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle'
},
In case you are looking for an answer in navigation version 5 and funcional components:
const getActiveRouteState = function (
routes: Route<string>[],
index: number,
name: string
) {
return routes[index].name.toLowerCase().indexOf(name.toLowerCase()) >= 0;
};
function CustomDrawerContent(
props: DrawerContentComponentProps<DrawerContentOptions>
) {
return (
<View style={{ flex: 1 }}>
<DrawerContentScrollView {...props}>
<View>
<DrawerItem
icon={({ color, size }) => <Icon type='AntDesign' name='home' />}
label='Home'
focused={getActiveRouteState(
props.state.routes,
props.state.index,
'Home'
)}
onPress={() => {
props.navigation.navigate('Home');
}}
/>
<DrawerItem
icon={({ color, size }) => (
<Icon type='AntDesign' name='minuscircle' />
)}
label='Test'
focused={getActiveRouteState(
props.state.routes,
props.state.index,
'Test'
)}
onPress={() => {
props.navigation.navigate('Test');
}}
/>
<DrawerItem
icon={({ color, size }) => (
<Icon type='AntDesign' name='logout' />
)}
label='LogOut'
onPress={async () => {
await logoutUser();
setLogginState(false);
}}
/>
</View>
</DrawerContentScrollView>
</View>
);
}
const AppDrawer = createDrawerNavigator();
const AppDrawerScreen = () => (
<AppDrawer.Navigator
drawerPosition='left'
drawerContent={(props) => <CustomDrawerContent {...props} />}
>
<AppDrawer.Screen
name='Home'
component={HomeScreen}
options={{ drawerLabel: 'Home' }}
/>
<AppDrawer.Screen name='Test' component={TestScreen} />
</AppDrawer.Navigator>
);
This will also help you in case you want to hide an option in the drawer.
You can create a modal which will do this for you. On click of logout -> display modal using visible attribute and on click of yes then close the modal -> navigate to login screen.
Just use AlertView bro that will help but in the above example if you have an header than this (this.props.navigation.navigate('LoginPage'))
will not make any senses
your view will load below the header
Here is a way to implement a logout in a drawer using a react-navigation library.
<Drawer.Screen
name="Logout"
component={<Any Dummy Component>}
listeners={({ navigation }) => ({
state: (e) => {
if (e.data.state.index === 3) {
// 3 is index of logout item in drawer
navigation.replace("Login")
}
}
})}
/>