Whenever I activate the onPress method by tapping on a message, the MessageScreen component just re-renders rather than displaying ChatScreen. This happens even if I replace ChatScreen with any other screen. Any help on the matter is much appreciated.
App.js
<NavigationContainer ref={containerRef} initialState={initialNavigationState}>
<Drawer.Navigator>
<Drawer.Screen name="Feed" component={BottomTabNavigator} options={{swipeEnabled: false}} />
<Drawer.Screen name="Settings" component={SettingsStack} options={{swipeEnabled: false}} />
</Drawer.Navigator>
</NavigationContainer>
BottomTabNavigator
const BottomTab = createBottomTabNavigator();
export default function BottomTabNavigator({ navigation, route }) {
{...HomeStack Code}
{...ProfileStack Code}
const MyMessagesStack = createStackNavigator();
function MessagesStack() {
return (
<MyMessagesStack.Navigator initialRouteName={"Messages"}
screenOptions={{headerShown: false}}>
<MyMessagesStack.Screen name="Messages" component={MessagesScreen} />
<MyMessagesStack.Screen name="Chats" component={ChatScreen} />
</MyMessagesStack.Navigator>
);
}
return (
<BottomTab.Navigator initialRouteName={INITIAL_ROUTE_NAME} >
<BottomTab.Screen
name="Home"
component={HomeStack}
options={{title: 'Feed'}}
/>
<BottomTab.Screen
name="Messages"
component={MessagesStack}
options={{title: 'Messages'}}
/>
<BottomTab.Screen
name="Profile"
component={ProfileStack}
options={{title: 'Profile'}}
/>
</BottomTab.Navigator>
);
}
MessageScreen.js
//flatscreen to render message components
</View>
<FlatList
data={Data}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => props.navigation.navigate('Chats')} >
<Message
image={item.image}
name={item.name}
lastMessage={item.message}
timeStamp={item.timestamp}
opened
/>
</TouchableOpacity>
)}
/>
The reason your components are remounting is because there are components defined inside other components:
function BottomTabNavigator() {
// Here
function MessagesStack() {
// ...
}
// ...
}
You need to define them outside to avoid that:
function MessagesStack() {
// ...
}
function BottomTabNavigator() {
// ...
}
Related
I have a drawer navigation configured in the following way:
const CustomDrawerContent = () => { return ( <DrawerItem label="Log out2" onPress={() => logOut()} /> ) }
const loginStack = () => (
<Stack.Navigator >
<Stack.Screen name='LandingScreen' component={LandingScreen} options={{headerShown: false}} />
<Stack.Screen name='LoginScreen' component={LoginScreen} options={{headerShown: false}} />
<Stack.Screen name='RegisterScreen' component={RegisterScreen} options={{headerShown: false}} />
</Stack.Navigator>
)
return (
<NavigationContainer>
<Drawer.Navigator
screenOptions={{
drawerStyle: { backgroundColor: 'white' },
drawerPosition: 'right'
}}>
{!user ? (
<Drawer.Screen
name="PublicStack"
component={loginStack}
options={{headerShown: false}}
/> )
:
(<>
<Drawer.Screen name='Search cocktails' component={HomeScreen} options={{ header: () => <Header/> }} />
<Drawer.Screen name='Profile' component={ProfileScreen} options={{ header: () => <Header/> }} />
<Drawer.Screen name='Publish a recipe' component={PublishRecipeScreen} options={{ header: () => <Header/> }} />
<Drawer.Screen name='Favorites' component={FavoritesScreen} options={{ header: () => <Header/> }} />
<Drawer.Screen name='Published recipes' component={PublishedRecipesScreen} options={{ header: () => <Header/> }} />
<Drawer.Screen name='Log out' component={CustomDrawerContent} options={{ header: () => <Header/> }} />
<Drawer.Screen name='CocktailDetailScreen' component={CocktailDetailScreen} options={{
header: () => <Header/>,
drawerLabel: () => null,
title: undefined
}} />
</>
)}
</Drawer.Navigator>
</NavigationContainer>
)
All screens work fine, but I want Log out to execute a logout function onPress. As I understand, I can't add this event listener directly on the screen component, so I followed this doc (https://reactnavigation.org/docs/drawer-navigator/#drawercontent) and tried a few different things:
I created the component CustomDrawerContent which is a DrawerItem.
If I pass CustomDrawerContent as the component to the Log out screen (as the code is right now), when I click on it I get redirected to a blank page that renders CustomDrawerContentcomponent, which isn't what I want.
If I pass CustomDrawerContent as drawerContent props to the drawer navigator, like the doc says (example below), all other screens dont render anymore, which is again not what I want.
<Drawer.Navigator drawerContent={(props) => <CustomDrawerContent />}>
{/* screens */}
</Drawer.Navigator>
If I put the drawer item together with the screens inside the navigator, the app throws the following error:
useNavigationBuilder.tsx:134 Uncaught Error: A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found 'DrawerItem'). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.
So how can I add the item to the drawer without 'overwritting' the screens?
Or is there other way to put a simple logout button in the drawer?
Full code can be found here: https://github.com/coccagerman/mixr
I have a navigator as below. I want to show the drawer when the user logged in. However, the isLogin will still be false after I login. As I am quit new to react native, is there any solution to this issue? Also, I want to know if there are any good sources for learning react native?
let isLogin = firebase.auth().currentUser ? true : false;
const HomeStack = () => {
return (
<Stack.Navigator
screenOptions={{
headerStyled: {
backgroundColor: primary,
},
headerLeftContainerStyle: {
paddingLeft: 20,
},
headerShown: false,
}}
initialRouteName="Home"
>
<Stack.Screen name="HomeScreen" component={Home} />
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
);
};
console.log(isLogin);
const LoginStack = () => {
return (
<Stack.Navigator
screenOptions={{
headerStyled: {
backgroundColor: primary,
},
headerLeftContainerStyle: {
paddingLeft: 20,
},
headerShown: false,
}}
initialRouteName="Login"
>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Signup" component={Signup} />
<Stack.Screen name="Home" component={Home} />
</Stack.Navigator>
);
};
const RootStack = () => {
return (
<NavigationContainer>
{/* {isLogin ? (
<Drawer.Navigator>
<Drawer.Screen name="Home" component={HomeStack} />
<Drawer.Screen name="Chat" component={Chat} />
</Drawer.Navigator>
) : (
<LoginStack />
)} */}
<LoginStack />
</NavigationContainer>
);
};
export default RootStack;
Because you initialize isLogin variable outside the component, you get value only one time. So you need to put declaring variable into the component.
const RootStack = () => {
let isLogin = firebase.auth().currentUser ? true : false;
return (
<NavigationContainer>
{/* {isLogin ? (
<Drawer.Navigator>
<Drawer.Screen name="Home" component={HomeStack} />
<Drawer.Screen name="Chat" component={Chat} />
</Drawer.Navigator>
) : (
<LoginStack />
)} */}
<LoginStack />
</NavigationContainer>
);
};
I am trying to use the navigation prop inside my Stack Navigator to open the drawer when the material icon is clicked. However when I click th button I recieve the error:
Undefined is not an object (evaluating navigation.openDrawer)
I am confused as I have passed the navigation prop into the 'App' function. Where am I going wrong here?
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createDrawerNavigator} from '#react-navigation/drawer';
import HomeScreen from './src/screens/HomeScreen';
import SecondScreen from './src/screens/SecondScreen.js';
import {MaterialIcons} from '#expo/vector-icons';
const Drawer = createDrawerNavigator();
const Stack = createStackNavigator();
const TheDrawer = () => {
return(
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="SecondScreen" component={SecondScreen} />
</Drawer.Navigator>
);
}
const App = ({navigation}) =>{
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"component={TheDrawer}
options={{headerTitle:
<View>
<MaterialIcons
name='menu'
onPress={() => navigation.openDrawer()} size={28}
/>
</View>
}}
/>
<Stack.Screen name="SecondScreen" component={SecondScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default App;
The navigation prop only exist inside a Navigator, and different Navigators will have different navigation props (the Stack Navigator navigation will NOT have the openDrawer method, for example). I think that what you want to accomplish is this:
const App = () => {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen
name="Home"
component={HomeScreen}
options={({ navigation }) => ({
headerTitle: (
<View>
<MaterialIcons name="menu" onPress={() => navigation.openDrawer()} size={28}/>
</View>
),
})}
/>
<Drawer.Screen name="SecondScreen" component={SecondScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
};
Or, if you want the menu button available on every page header:
const App = () => {
return (
<NavigationContainer>
<Drawer.Navigator
initialRouteName="Home"
screenOptions={({ navigation }) => ({
headerTitle: (
<View>
<MaterialIcons name="menu" onPress={() => navigation.openDrawer()} size={28} />
</View>
),
})}
>
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="SecondScreen" component={SecondScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
};
Source: https://reactnavigation.org/docs/headers/
If you need to access more pages inside this two (Home, SecondScreen), you can create it like this:
const HomeScreen = () => {
<Stack.Navigator>
<Stack.Screen name="Home1" component={HomeScreen1} />
<Stack.Screen name="Home2" component={HomeScreen2} />
<Stack.Screen name="Home3" component={HomeScreen3} />
</Stack.Navigator>
}
Source: https://reactnavigation.org/docs/nesting-navigators
Currently I am exporting a component
export default function ChooseNamespace() {
return (
<View>
<Text>HELLO</Text>
</View>
);
}
And importing it here:
import ChooseNamespace from "./screens/ChooseNamespace";
const AppDrawerNavigator = () => {
console.log(storeState.userNamespace);
if (storeState.userNamespace === null) {
return <ChooseNamespace />;
} else {
return (
<AppDrawer.Navigator
drawerContent={(props) => <AppDrawerContent {...props} />}
drawerContentOptions={{
activeTintColor: "blue",
labelStyle: {
color: "white",
},
}}
>
<AppDrawer.Screen
name="Chat"
children={(props) => <ChatScreen name="Blindly Chat" {...props} />}
/>
<AppDrawer.Screen
name="Profile"
children={(props) => <ProfileScreen name="Profile" {...props} />}
/>
<AppDrawer.Screen
name="DirectMessages"
children={(props) => <DMScreen {...props} />}
/>
<AppDrawer.Screen
name="Threads"
children={(props) => <ThreadsScreen {...props} />}
/>
<AppDrawer.Screen
name="Settings"
children={(props) => <SettingsScreen {...props} />}
/>
<AppDrawer.Screen
name="Support"
children={(props) => <SupportScreen {...props} />}
/>
</AppDrawer.Navigator>
);
}
};
Which is then throwing the error
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of ChooseNamespace.
Can someone try an explain the issue here? I am having a bit of trouble finding it.
const ChooseNamespace =()=> {
return (
<View>
<Text>HELLO</Text>
</View>
);
}
export default ChooseNamespace
Try this code. ES6 doesn't allow export default const. You must declare the constant first then export it. Or if WANT to have a function then do this:
function ChooseNamespace() {
return (
<View>
<Text>HELLO</Text>
</View>
);
}
export default ChooseNamespace;
Can you try this.
return (
<>
{
storeState.userNamespace === null?
<ChooseNamespace/>:
<YourDrawerView/>
}
</>
);
I am using tabnavigator and stacknavigator, and after my connection I cannot be redirected to my home navigation unless I add Home to my stacknavigator, but tabnavigator disappears. I would like to know what I should do to be sent to my home navigation, so that my tabnavigator does not disappear.
const [user, setUser] = useState(false);
useEffect(()=> {
setTimeout(() => {
User()
console.log("Update")
}, 500);
},)
const User = async() => {
const value = await AsyncStorage.getItem('#user')
if (value === null) {
console.log('disconnecte')
}else {
setUser(true)
console.log("online")
navigation.navigate('Home')
}
}
return (
<NavigationContainer>
{user === false ? <Authnavigator/> : <Stacknavigator /> }
</NavigationContainer>
)
}
StackNavigator
export default function Stacknavigator() {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerShown: false
}}>
<Stack.Screen name="TabNavigator" component={TabNavigator} />
<Stack.Screen name="Details" component={Details} />
</Stack.Navigator>
)
}
Authnavigator
export default function Authnavigator() {
return (
<AuthStack.Navigator screenOptions={{
headerShown: false
}}>
<AuthStack.Screen name="Login" component={Login} />
</AuthStack.Navigator>
)
}
Update :
the login navigation to Home works but then to go to the Details view it does not work
export default function Stacknavigator() {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerShown: false
}}>
<Stack.Screen name="Tabnavigator" component={Tabnavigator} />
<Stack.Screen name="Details" component={Details} />
</Stack.Navigator>
)
}
Tabnavigator
export default function Tabnavigator() {
return (
<Tab.Navigator
initialRouteName="Home"
activeColor="#f0edf6"
inactiveColor="#6D6D6D"
barStyle={{ backgroundColor: '#171717' }}
>
<Tab.Screen name="Home" component={Home} options= {{
tabBarIcon: ({ color }) => (
<FontAwesome name="home" size={20} color={(color)} />
)
}} />
<Tab.Screen name="Recherche" component={Search} options={{
tabBarIcon: ({color}) => (
<FontAwesome name="search" size={20} color={(color)} />
)
}} />
<Tab.Screen name="Favorie" component={Favorie} options={{
tabBarIcon: ({color}) => (
<FontAwesome name="star" size={20} color={(color)} />
)
}}/>
</Tab.Navigator>
)
}
const Tab = createMaterialBottomTabNavigator();
Authnavigator
export default function Authnavigator() {
return (
<AuthStack.Navigator screenOptions={{
headerShown: false
}}>
<AuthStack.Screen name="Login" component={Login} />
<AuthStack.Screen name="Tabnavigator" component={Tabnavigator} />
</AuthStack.Navigator>
)
}
You don't have an initialRouteName Home in your Stack.Navigator. You need to point it to TabNavigator so that your code should look like this.
export default function Stacknavigator() {
return (
<Stack.Navigator
initialRouteName="TabNavigator"
screenOptions={{
headerShown: false
}}>
<Stack.Screen name="TabNavigator" component={TabNavigator} />
<Stack.Screen name="Details" component={Details} />
</Stack.Navigator>
)
}