React-Navigation v5: How to get screen's state from header - react-native

According to the React-Navigation docs (https://reactnavigation.org/docs/header-buttons/), in the header, we can interact with screen's state:
function StackScreen() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={({ navigation, route }) => ({
headerTitle: props => <LogoTitle {...props} />,
})}
/>
</Stack.Navigator>
);
}
function HomeScreen({ navigation }) {
const [count, setCount] = React.useState(0);
React.useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<Button onPress={() => setCount(c => c + 1)} title="Update count" /> // <===== SET STATE
),
});
}, [navigation]);
return <Text>Count: {count}</Text>;
}
It works fine when I set screen's state from header. But when I try to get screen's state from header, it's always return the initial value of state.
<Button
onPress={() => {
setCount(c => c + 1);
alert(count)
}}
title="Update count" />
How can I get the current state of screen from header?

I've had the solution:
Problem is in the React.useLayoutEffect() function.
I need to pass [count] to the second argument of this function instead of [navigation]:
React.useLayoutEffect(()=>{
...
}, [count]);
Then, it works fine

Related

React-Native, Navigation, Drawers and passing inline functions with props

I have a Drawer Navigator nested inside a Stack navigator. It all works fine but I get the following warning:-
Looks like you're passing an inline function for 'component' prop for the screen 'Home' (e.g. component={() => }). Passing an inline function will cause the component state to be lost on re-render and cause perf issues since it's re-created every render. You can pass the function as children to 'Screen' instead to achieve the desired behaviour.
Here is the code.
const [isAuthenticated, setIsAuthenticated] = React.useState(false);
const [initialRoute, setInitialRoute] = React.useState("");
const handleSignOut = () => {
// TODO implement sign out mechanism
};
return (
<NavigationContainer>{
<Stack.Navigator initialRouteName="Sign In" >
{isAuthenticated ? (
<Stack.Screen name="Home"
component={() => <HomeDrawer initialRoute={initialRoute} handleSignOut={handleSignOut}/>}
options={({ route, navigation }) => ({
headerTitle: getFocusedRouteNameFromRoute(route),
headerLeft: () => (
<Button
title="Menu"
onPress={() =>
navigation.dispatch(DrawerActions.toggleDrawer())
}
/>
),
headerRight: () => (
<Button onPress={handleSignOut} title="Sign Out" />
),
})}
/>
) : (
...
I fixed the warning by moving the component, but now the Drawer has stop working completely and I get this error:-
The action 'TOGGLE_DRAWER' was not handled by any navigator.
Is your screen inside a Drawer navigator?
Here is the new code.
const [isAuthenticated, setIsAuthenticated] = React.useState(false);
const [initialRoute, setInitialRoute] = React.useState("");
const handleSignOut = () => {
// TODO implement sign out mechanism
};
const Draw = () => {<HomeDrawer initialRoute={initialRoute} handleSignOut={handleSignOut}/>};
return (
<NavigationContainer>{
<Stack.Navigator initialRouteName="Sign In" >
{isAuthenticated ? (
<Stack.Screen name="Home"
component={Draw}
options={({ route, navigation }) => ({
headerTitle: getFocusedRouteNameFromRoute(route),
headerLeft: () => (
<Button
title="Menu"
onPress={() =>
navigation.dispatch(DrawerActions.toggleDrawer())
}
/>
),
headerRight: () => (
<Button onPress={handleSignOut} title="Sign Out" />
),
})}
/>
) : (
...
I'm new to React-Native and on a steep learning curve. I've read a bunch of tutorials but stumped on this one. I can see I need to pass initialRoute at least, but not sure how to do that. A fix with an explanation would be great.
I found the answer in the end. It was pretty straight forward, but not when there are so many things to learn at once :-) When I moved the component like so it all worked.
<Stack.Screen name="Home"
options={({ route, navigation }) => ({
headerTitle: getFocusedRouteNameFromRoute(route),
headerLeft: () => (
<Button
title="Menu"
onPress={() =>
navigation.dispatch(DrawerActions.toggleDrawer())
}
/>
),
headerRight: () => (
<Button onPress={handleSignOut} title="Sign Out" />
),
})}
>
{(props) => (
<HomeDrawer {...props} initialRoute={initialRoute} handleSignOut={handleSignOut} />
)}
</Stack.Screen>
<Stack.Screen name="TEST" component={TestScreen}
screenOptions={{ headerShown: true }}
options={{ headerTitle: "Hello" }}
/>

Can't pass parameters through React Navigation TabBar

Can't send any parameters with react-navigation 5x.
Tried everything on documentation. Don't know what's wrong.
I will share you all my route structure.
My ApplicationNavigator.js: I'm using drawer content with tabbar navigator at the same time. OrderNavigator is a tabnavigator, Dashboard is stacknavigator.
const ApplicationNavigator = () => {
const [isApplicationLoaded, setIsApplicationLoaded] = useState(false)
const applicationIsLoading = useSelector((state) => state.startup.loading)
return (
<Drawer.Navigator
drawerStyle={{ width: '100%' }}
drawerContent={(props) => <DrawerContent {...props} />}
>
<Drawer.Screen name="Dashboard" component={DashboardNavigator} />
<Drawer.Screen name="Order" component={OrderNavigator} />
</Drawer.Navigator>
)
}
export default ApplicationNavigator
My OrderNavigator.js: This is my tab.screen structure.
const ScreenA = () => {
return (
.....
<Stack.Navigator>
<Stack.Screen
options={headerStyle_1}
name="Orders"
component={IndexOrderContainer}
/>
</Stack.Navigator>
)
}
const ScreenB = () => {
....
return (
<Stack.Navigator>
<Stack.Screen
options={headerStyle_1}
name="New Order"
component={AddOrderContainer}
/>
</Stack.Navigator>
)
}
const OrderNavigator = () => {
return (
<Tab.Navigator headerMode={'none'}>
<Tab.Screen name="OrderList" component={ScreenA} />
<Tab.Screen name="NewOrder" component={ScreenB} />
</Tab.Navigator>
)
}
export default OrderNavigator
I'm trying to make redirect with code below. I'm using it with my order listing page, and trying to redirect to "order detail" page onclick. (with tab navigator.)
<TouchableOpacity
onPress={() =>
navigation.navigate('NewOrder', {
params: { user: 'jane' },
})
}
style={[styles.ListItem]}
button
key={`order${index}`}
>
And my response, when try to navigate with parameters is.
But you can see that my params is "undefined" with response.
const AddOrderContainer = ({ route, navigation }) => {
useEffect(() => {
console.log(route)
}, [route])
....
....
Object {
"key": "New Order-mafygLVPzFO1rVOhj1zVH",
"name": "New Order",
"params": undefined,
}

onPress to navigate on a screen not navigating

I am a beginner in React Native and I would like to navigate on a screen when I press the Button of my Navigator screen but it's not working.
Because when I press the Button in TabOneStack.Screen nothing is happening I don't understand, I would like to navigate to TabTwoScreen. I use React Navigation 5.6.1.
const BottomTab = createBottomTabNavigator<BottomTabParamList>();
export default function BottomTabNavigator() {
return (
<BottomTab.Navigator
initialRouteName="TabOne">
<BottomTab.Screen
name="TabOne"
component={TabOneNavigator}
/>
<BottomTab.Screen
name="TabTwo"
component={TabTwoNavigator}
options={{
tabBarLabel: 'Autour de moi',
tabBarIcon: ({ color }) => <TabBarIcon name="ios-navigate" color={color} />,
}}
/>
</BottomTab.Navigator>
);
}
const TabOneStack = createStackNavigator<TabOneParamList>();
function TabOneNavigator() {
return (
<TabOneStack.Navigator>
<TabOneStack.Screen
name="TabOneScreen"
component={TabOneScreen}
options={({ navigation }) => ({
headerTitle: 'Rejoindre', headerRight: () => (
<Button onPress={() => navigation.navigate('TabTwoScreen')}
icon={
<Icon
name='ios-log-in'
type='ionicon'
size={15}
color="white"
/>
}
/>
),
})}
/>
</TabOneStack.Navigator>
);
}
const TabTwoStack = createStackNavigator<TabTwoParamList>();
function TabTwoNavigator() {
return (
<TabTwoStack.Navigator>
<TabTwoStack.Screen
name="TabTwoScreen"
component={TabTwoScreen}
options={{ headerTitle: 'Autour de moi' }}
/>
</TabTwoStack.Navigator>
);
}
Why when I press the Button in TabOneStack.Screen nothing is happening?
Thank you in advance
You need to use props for navigation and you can try this
options={({ navigation }) => ({
headerTitle: 'Rejoindre', headerRight: props => (
<Button onPress={() => props.navigation.navigate('TabTwoScreen')}

React Navigation V5 does not receive focused parameter when using custom bottomTab component

I am currently trying to implement a custom tabBar design into my react native app which is using React Navigation 5 as the navigation library. Everything is working correctly, except that my tabBarIcons don't receive any props, so i cannot determine whether i have to show the active or inactive tabIcon. Whenever i use a default tabbar i do receive the props, so there must be something wrong in my custom tabbar. I did follow the docs though, and only find the instruction to emit the 'tabPress' event. I do however think that i should emit more events to get the correct focused prop. I have set up the navigator like this:
const Tabs = createBottomTabNavigator();
export default () => (
<Tabs.Navigator tabBar={TabBarComponent} initialRouteName="Home">
<Tabs.Screen
name="Home"
component={HomeScreen}
options={{
tabBarIcon: ({ focused }) => {
// The props here are {}, so focused is undefined.
const icon = focused
? require('images/iconOverviewRed.png')
: require('images/iconOverviewGrey.png');
return <Image source={icon} />;
},
}}
/>
<Tabs.Screen
name="Overview"
component={OverviewScreen}
options={{
tabBarIcon: props => {
console.log(props);
return <Image source={require('images/logoRed.png')} />;
},
}}
/>
<Tabs.Screen
name="Account"
component={AccountScreen}
options={{
tabBarIcon: ({ focused }) => {
const icon = focused
? require('images/iconAccountRed.png')
: require('images/iconAccountGrey.png');
return <Image source={icon} resizeMethod="resize" />;
},
}}
/>
</Tabs.Navigator>
);
And this is my custom tabBar compnent:
const TabBar = ({ navigation, state, descriptors }: any) => {
return (
<View style={styles.container}>
{state.routes.map((route: any) => {
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!event.defaultPrevented) {
navigation.dispatch({
...TabActions.jumpTo(route.name),
target: state.key,
});
}
};
return (
<TabIcon
key={route.key}
Icon={descriptors[route.key].options.tabBarIcon}
onPress={onPress}
isBig={route.name === 'Home'}
/>
);
})}
</View>
);
};
const TabIcon = ({ onPress, Icon, key, isBig }: any) => {
return (
<TouchableWithoutFeedback key={key} onPress={onPress}>
<View style={isBig ? styles.bigTab : styles.defaultTab} key={key}>
<Icon />
</View>
</TouchableWithoutFeedback>
);
};
Thanks in advance.
descriptors[route.key].options just gives you the options as you have specified them. If you log the value of descriptors[route.key].options.tabBarIcon, you'll see that it prints the function that you have specified.
In your custom tab bar, it's upto you to use the option as you need. Since it's a function here, you'll have to call it and pass desired arguments.
descriptors[route.key].options.tabBarIcon({ focused: state.index === index })
This also means that you fully control the option. You can put whatever type you'd like, function, a require statement directly etc. and then use that. You also don't have to call it tabBarIcon, you can call it whatever you want.

React navigation 5.0 header button

I trying to add right navigation header button directly on from Component, and implementation steps have been changed navigation 5.0 version, there is one method that provide add button with method
function HomeScreen({ navigation }) {
const [count, setCount] = React.useState(0);
navigation.setOptions({
headerRight: () => (
<Button onPress={() => setCount(c => c + 1)} title="Update count" />
),
});
return <Text>Count: {count}</Text>;
}
but need to implement on it
export default class HomeScreen extends Component {
constructor() {
super()
}
render() {
return ()
}
}
You can do this in your component constructor
this.props.navigation.setOptions({
headerRight: () => <Button />
});
Try this
<Stack.Screen
code..//
options={{
code...//
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
}}
/>