How to Pass and Read param in top bar nested navigators? - reactjs

const TopTab = createMaterialTopTabNavigator();
export const TopNavigator = () => {
return (
<TopTab.Navigator>
<TopTab.Screen name="profile" component={profile} />
<TopTab.Screen name="setting" component={setting} />
</TopTab.Navigator>
);
}
const MainMenuStackNavigator = createStackNavigator();
export const MainMenuNavigator = () => {
return (
<MainMenuStackNavigator.Navigator screenOptions={defaultNavOptions}>
<MainMenuStackNavigator.Screen name="home" component={homeScreen}/>
<MainMenuStackNavigator.Screen name="tab" component={TopNavigator }/>
</MainMenuStackNavigator.Navigator>
);
};
function HomeScreen() {
return (
<View>
<Text>Home!</Text>
<Button
title="Go to Top Navigator Screen"
onPress={() => {props.navigation.navigate({name: "tab",params:{data: "Testing"}});}}
/>;
</View>
);
}
export default function App() {
return (
<NavigationContainer>
< MainMenuNavigator />
</NavigationContainer>
);
}
Need to pass Data from 'home' screen to both 'Profile' and 'Setting' screen
i know how pass data from home screen to any one of those screen,
props.navigation.navigate(tab, {
screen: "profile",
params: { data: 'testing'}
});
but here my question is ' how to send data to both screens(profile and setting)?

Related

The action 'NAVIGATE' with payload {"name":"Recette"} was not handled by any navigator

I'm trying to navigate between two screens, and i don't know what's happening here.
I would like some help please thanks.
App.js
const Stack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false
}}
initialRouteName={'Home'}
>
<Stack.Screen options={{headerShown: false}}name="Home" component={Tabs} />
</Stack.Navigator>
</NavigationContainer>
)
}
export default App;`
Home.js
const Home = ({ navigation }) => {
function renderRecetteList() {
const renderItem = ({ item }) => (
<TouchableOpacity
onPress={() => navigation.navigate("Recette" )}
>
</TouchableOpacity>
)
return (
<FlatList
data={recette}
keyExtractor={item => `${item.id}`}
renderItem={renderItem}
contentContainerStyle={{
paddingHorizontal: SIZES.padding * 2,
paddingBottom: 30
}}
/>
)
}
return(
<SafeAreaView style = {styles.container}>
{renderRecetteList()}
</SafeAreaView>
)
}
Recette.js
const Recette = ({ navigation }) => {
return(
<View>
<Text>Search</Text>
</View>
)
}
export default Recette;
The error
The action 'NAVIGATE' with payload {"name":"Recette"} was not handled by any navigator.
Do you have a screen named 'Recette'?
You can only navigate to components that are defined as a Screen in a react Navigator. In order to use navigate to go to Recette you need to define it as a Screen as you did with Home.
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false
}}
initialRouteName={'Home'}
>
...
<Stack.Screen name="Recette" component={Recette} />
</Stack.Navigator>
</NavigationContainer>
)
}

React Native Expo - show different NavigationContainer depending on whether user is logged in or not

App.js:
const Stack = createNativeStackNavigator();
const Drawer = createDrawerNavigator();
export default function App() {
let isLoggedIn = false;
setInterval(function () {
auth.onAuthStateChanged((user) => {
isLoggedIn = user !== null;
});
}, 50);
if (!isLoggedIn) {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="LoginScreen" component={LoginScreen} />
<Stack.Screen name="StartScreen" component={StartScreen} />
</Stack.Navigator>
</NavigationContainer>
);
} else if (isLoggedIn) {
return (
<>
<NavigationContainer>
<Drawer.Navigator initialRouteName="StartScreen">
<Drawer.Screen name="StartScreen" component={StartScreen} />
<Drawer.Screen name="LoginScreen" component={LoginScreen} />
</Drawer.Navigator>
</NavigationContainer>
</>
);
}
}
const styles = StyleSheet.create({});
AppRegistry.registerComponent("myApp", () => App);
My goal is to only display the Drawer.Navigator when the user is logged in.
I tried updating a boolean isLoggedIn to change the return statement but it is no working, altough the boolean is changing values.
Any ideas on how to approach this?
Thanks in advance
Refactor code as below:
//...Other top-level imports
import { ActivityIndictor } from "react-native";
const Stack = createNativeStackNavigator();
const Drawer = createDrawerNavigator();
export default function App() {
const [isLoggedIn, setLoggedIn] = React.useState(null);
const authenticateUser = () => {
auth.onAuthStateChanged((user) => {
if (user) {
setLoggedIn(true);
} else {
setLoggenIn(false);
}
});
};
// Check authentication state when the app launched
React.useEffect(() => {
if (!isLoggedIn) {
authenticateUser();
}
}, [isLoggedIn]);
/** While user is authenticating, show progress indicator*/
if (isLoggedIn === null) return <ActivityIndicator />;
return (
<NavigationContainer>
{isLoggedIn ? (
<Drawer.Navigator initialRouteName="StartScreen">
<Drawer.Screen name="StartScreen" component={StartScreen} />
<Drawer.Screen name="LoginScreen" component={LoginScreen} />
</Drawer.Navigator>
) : (
<Stack.Navigator>
<Stack.Screen name="LoginScreen" component={LoginScreen} />
<Stack.Screen name="StartScreen" component={StartScreen} />
</Stack.Navigator>
)}
</NavigationContainer>
);
}
const styles = StyleSheet.create({});
AppRegistry.registerComponent("myApp", () => App);
Click on the link https://reactnavigation.org/docs/auth-flow to read about how auth flows work in react native.

Passing state to parent and then to child

I have app.js which assigns values to a state. I am then trying to pass that state to a Home screen which renders a Screen1. I need this state in Screen1.
You can see I am trying to display the users latitude and longitude on Screen1 but I am not able to render anything.
I have listed the code below as well as in this snack demo here. The goal is for the state to go from App.js -> Home.js -> Screen1.js
EDIT:: I might use React native context but never have before. If the current way I am doing it is considered a poor practice then context is the way to go.
Home.js
export default function Home({ navigation, latitude, longitude }) {
return (
<View style={styles.container}>
<Screen1 />
</View>
);
}
Screen1.js
export default class Screen1 extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>latitude:</Text>
<Text style={styles.paragraph}>{this.props.latitude}</Text>
<Text style={styles.paragraph}>longitude:</Text>
<Text style={styles.paragraph}>{this.props.longitude}</Text>
</View>
);
}
}
export default function MyTabs() {
const [latitude, setLatitude] = useState(null);
const [longitude, setLongitude] = useState(null);
const permissionAlert = () => {
Alert.alert(
'You did not allow location permissions',
'Please go to settings and allow location permissions for full functionality',
[
],
{
cancelable: true
}
);
}
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
permissionAlert();
return;
}
let location = await Location.getCurrentPositionAsync({});
setLatitude(location.coords.latitude)
setLongitude(location.coords.longitude);
})();
}, []);
console.warn("latitude: ", latitude);
console.warn("longitude: ", longitude);
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home">
<Stack.Screen name="Home" options={{headerShown: false}}>
{(props) => <Home {...props} latitude={latitude} longitude={longitude} />}
</Stack.Screen>
<Stack.Screen name="Screen1" options={{headerShown: false}}>
{(props) => <Screen1 {...props} latitude={latitude} longitude={longitude} />}
</Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
);
}
You need to pass latitude/longitude from MyTabs -> Home -> Screen1. Currently, you're setting them on Home, but not on Screen1. To fix, add the props to <Screen1 /> in Home.js
export default function Home({ navigation, latitude, longitude }) {
return (
<View style={styles.container}>
<Screen1 latitude={latitude} longitude={longitude} />
</View>
);
}
It's also worth noting that you're rendering Screen1 both
directly within MyTabs
indirectly in MyTabs, when you render Home
Basically, make sure you always pass all expected props when you render a component and you should be good to go!

Drawer's screen component is re-rendering when a drawer opens/closes

I have the main Stack navigator with multiple screens and one of those screens is a nested Drawer navigator with dynamically created screens.
<Provider store={store}>
<NavigationContainer onStateChange={onStateChange}>
<Stack.Navigator initialRouteName="Launch">
<Stack.Screen name="Launch" component={Launch} />
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="ChatBox" component={ChatBox} /> /* <- Here is the drawer navigator screen */
</Stack.Navigator>
</NavigationContainer>
</Provider>
As you can see in the example gif the ChatComponent is always re-rendering when drawer actions were. It does even if the current screen hasn't changed in the drawer's list.
<Drawer.Navigator>
{userChannels.map((channel, index) => {
const chatName = userChannels[index].friendlyName;
const screen = React.memo(() => (
<View style={{ flex: 1 }}>
<Header text={chatName} />
<ChatComponent
channel={channel}
loadEarlier={loadEarlier}
isLoadingEarlier={isLoadingEarlier}
onLoadEarlier={onLoadEarlier}
savedMessages={savedMessages}
onSend={onSend}
isTyping={isTyping}
user={{
_id: me.member.sid || 0,
}}
onInputTextChanged={async () => {
currentChannel &&
(await currentChannel.typing());
}}
enterChannel={() =>
enterChannel(channel.uniqueName)
}
/>
</View>
));
return (
<Drawer.Screen
key={index}
name={chatName || String(index)}
component={screen}
/>
);
})}
</Drawer.Navigator>
The header component is:
export default (props) => {
const { text } = props;
const navigation = useNavigation();
const logout = async () => {
try {
await AsyncStorage.removeItem('accessToken');
await AsyncStorage.removeItem('refreshToken');
} catch (e) {
//
}
navigation.navigate('Login');
};
return (
<View style={styles.header}>
<TouchableOpacity
onPress={() => navigation.dispatch(DrawerActions.openDrawer())}>
<Icon name="menu" color="#FFF" size={18} />
</TouchableOpacity>
{text && <Text style={styles.headerText}>{text}</Text>}
<Text style={styles.headerText} onPress={logout}>
Logout
</Text>
</View>
);
};
The chat component is:
const ChatComponent = React.memo((props) => {
const {
channel,
loadEarlier,
isLoadingEarlier,
onLoadEarlier,
savedMessages,
onSend,
isTyping,
user,
onInputTextChanged,
enterChannel,
} = props;
useEffect(() => {
console.log('render ChatComponent');
enterChannel();
}, []);
return (
<GiftedChat
inverted={false}
loadEarlier={loadEarlier}
isLoadingEarlier={isLoadingEarlier}
onLoadEarlier={onLoadEarlier}
messages={savedMessages}
onSend={onSend}
user={user}
onInputTextChanged={onInputTextChanged}
isTyping={isTyping}
renderBubble={renderBubble}
renderTime={renderTime}
renderInputToolbar={renderInputToolbar}
/>
);
});
Here is the onStateChange of NavigationContainer
How to avoid unnecessary renders of a ChatComponent?

Unable to navigate to drawer stack in react navigation 5

I have an app that has a bottom tab navigator inside a drawer navigator as my Home Screen and I also have a stack navigator comprising of my auth screens (including login screen).
I am unable to navigate from my login screen to my Home stack screen after a successful login.
I get the error - the action navigate with payload was not handled by any navigator.
Please see my code. How can I resolve this
Navigation file - Navigation.js
const MainTabScreen = () => (
<Tab.Navigator
initialRouteName="Main"
activeColor="#fff"
>
<Tab.Screen
name="Main"
component={Main}
options={{
tabBarLabel: 'Home',
tabBarColor: "#009387",
tabBarIcon: ({ color }) => (
<Icon name="md-home" color={color} size={26} />
),
}}
/>
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{
tabBarLabel: 'Profile',
tabBarColor: "#694fad",
tabBarIcon: ({ color }) => (
<Icon name="md-contact" color={color} size={26} />
),
}}
/>
<Tab.Screen
name="Explore"
component={ExploreScreen}
options={{
tabBarLabel: 'Account',
tabBarColor: "#d02860",
tabBarIcon: ({ color }) => (
<Icon name="md-planet" color={color} size={26} />
),
}}
/>
</Tab.Navigator>
)
// HOME STACK SCREEN
const DrawerScreen = () => (
<Drawer.Navigator drawerContent={props => <DrawerContent {...props} /> } >
<Drawer.Screen name='MainTab' component={MainTabScreen} />
</Drawer.Navigator>
);
const AuthStack = createStackNavigator();
const AuthStackScreen = ({navigation}) => (
<AuthStack.Navigator headerMode='none'>
<AuthStack.Screen name="SplashScreen" component={SplashScreen} />
<AuthStack.Screen name="SignInScreen" component={SignInScreen} /> // LOGIN SCREEN
<AuthStack.Screen name="SignUpScreen" component={SignUpScreen} />
</AuthStack.Navigator>
);
const RootStack = createStackNavigator();
const RootStackScreen = () => {
const [isLoading, setIsLoading] = React.useState(true);
const [user, setUser] = React.useState(null);
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const bootstrapAsync = async () => {
let userToken;
try {
userToken = await AsyncStorage.getItem('FBIdToken');
console.log("toen", userToken)
if(userToken !== null) {
setIsLoading(!isLoading);
setUser(userToken);
} else {
setIsLoading(!isLoading);
}
} catch (e) {
console.log(error)
}
// After restoring token, we may need to validate it in production apps
};
bootstrapAsync();
}, []);
return (
<RootStack.Navigator
headerMode="none"
screenOptions={{ animationEnabled: false }}
mode="modal"
>
{isLoading ? (
<RootStack.Screen name="Loading" component={LoadingScreen} />
) : user ? (
<RootStack.Screen name="DrawerScreen" component={DrawerScreen} /> //HOME STACK ) : (
<RootStack.Screen name="AuthStackScreen" component={AuthStackScreen} />
)}
</RootStack.Navigator>
);
};
export default RootStackScreen;
App.js
<Provider store={store}>
<NavigationContainer ref={navigationRef}>
<RootStackScreen />
</NavigationContainer>
</Provider>
Login.js
loginHandle = () => {
this.props.navigation.navigate("DrawerScreen")
};
I suspect that in your RootStackScreen component, your condition isLoading is true so the route to your DrawerScreen does not exists when you navigate. Try removing the condition to be sure. Then if this isthe reason, yu could try applying your condition to the component you pass to your DrawerScreen instead, something like :
<RootStack.Screen name="DrawerScreen" component={isLoading ? LoadingScreen : DrawerScreen} />

Resources