didFocus doesn't work while navigating from another screen - react-native

I am using "react-navigation 3.11.0" with my react native expo application and I have below structure of navigation.
const orderStackNavigator = createStackNavigator(
{
orders: {
screen: Orders
},
orderdetail: {
screen: OrderDetail
},
ordermoredetails: {
screen: OrderMoreDetails
},
ordernotes: {
screen: OrderNotes
},
orderbillingdetails: {
screen: OrderBillingDetails
},
orderdeliverydetails: {
screen: OrderDeliveryDetails
}
},
{
//headerMode: 'none'
defaultNavigationOptions: {
headerStyle: [styles.headerStyle]
}
}
);
const inventoryManagerStackNavigator = createStackNavigator(
{
categories: {
screen: Categories
},
products: {
screen: Products
},
editProduct: {
screen: EditProduct
}
},
{
//headerMode: 'none'
defaultNavigationOptions: {
headerStyle: [styles.headerStyle]
}
}
);
const tabNavigator = createBottomTabNavigator(
{
orders: {
screen: orderStackNavigator,
},
inventory: {
screen: inventoryManagerStackNavigator,
},
},
{
order: ["orders","inventory"],
animationEnabled: true,
tabBarOptions: {
activeTintColor: "#026AC2",
inactiveTintColor: "#86AAC2",
labelStyle: { fontFamily: "ClearSans-Regular" }
//iconStyle: { fontFamily: 'ClearSans-Bold' }
}
}
);
const mainStackNavigator = createStackNavigator(
{
login: {
screen: Login
},
oAuth: {
screen: OAuth
},
tabs: {
screen: tabNavigator
}
},
{
initialRouteName: "login",
headerMode: "none"
}
);
const AppContainer = createAppContainer(mainStackNavigator);
export default AppContainer;
I am facing issue like if I navigate from ordermoredetails to editProduct it will not add didFocus listener to navigation. If I once navigate to inventory and then editProduct it will work as expected even from ordermoredetails but if user go to ordermoredetails and navigate to editProduct it do not work. Below is my code for editProduct for adding Listener.
componentDidMount() {
const { navigation } = this.props;
this.focusListener = navigation.addListener("didFocus", () => {
if (this.props.needToReload == true) {
//do the stuff
}
});
}
componentWillUnmount() {
// Remove the event listener
if (this.focusListener != null && this.focusListener.remove)
this.focusListener.remove();
}
Can any one please let me know how can i fix this and make didFocus call every time component loads?

changing "didFocus" to "focus" inside navigation.addListener worked for me.

"didFocus" is not working with me. Please try "focus". Below is the sample code which is working with me. I have version: "#react-navigation/native": "^5.7.6",
componentDidMount() {
this.focusListener = this.props.navigation.addListener('focus', () => {
// your logic will go here
});
}
componentWillUnmount() {
// Remove the event listener
this.focusListener.remove();
}

Related

navigation.push not adding a new route

navigate works fine however when I replace it with push, it stops working.
<Text
onPress={() => {
//works
this.props.navigation.navigate("VideoPlayer", { id });
// doesn't work
this.props.navigation.push("VideoPlayer", { id });
}}
style={styles.text}
>
{title}
</Text>
How can I get push to work so I can remount components?
I am using a Drawer with a new stack for each route so I can use headers:
const config = {
initialRouteName: "Home",
contentOptions: {
activeTintColor: "#e91e63",
itemStyle: {
flexDirection: "row-reverse"
}
},
drawerWidth: 300,
drawerPosition: "right"
};
const withHeader = (
screen: Function,
routeName: string,
Header
): StackNavigator =>
createStackNavigator(
{
[routeName]: {
screen,
navigationOptions: ({ routeName, props }) => ({
header: props => <Header {...props} />
})
}
},
{
transparentCard: true
}
);
const routes = {
Home: {
screen: withHeader(HomeScreen, "Home", BasicHeader)
},
Links: {
screen: withHeader(LinksScreen, "Links", DrawerHeader)
},
Settings: {
screen: withHeader(SettingsScreen, "Settings", DrawerHeader)
},
VideoEpisodes: {
screen: withHeader(VideoEpisodesScreen, "Video Episodes", DrawerHeader)
},
VideoPlayer: {
screen: withHeader(VideoPlayerScreen, "Video Player", DrawerHeader)
},
TestYourself: {
screen: withHeader(TestYourselfScreen, "Test Yourself", DrawerHeader)
},
MyResults: {
screen: withHeader(MyResultsScreen, "My Results", DrawerHeader)
},
BookmarkedVideos: {
screen: withHeader(
BookmarkedVideosScreen,
"Bookmarked Videos",
DrawerHeader
)
},
Search: {
screen: withHeader(SearchScreen, "Search", DrawerHeader)
},
About: {
screen: withHeader(AboutScreen, "About", DrawerHeader)
}
};
const AppNavigator = createDrawerNavigator(routes, config);
export default createAppContainer(AppNavigator);
.push() is a StackAction method, you have to use a stackNavigator in order to use it

I have a problem in my code when updating react-navigation v2 to v3

I want to update the react-navigation library V2 to V3 and change part of my code thinking that there would not be any problems but it turns out that I have problems creating createStackNavigator with a screen of type createDrawerNavigator and that in turn contains createBottomTabNavigator.
my code that works with the previous version was:
export const createRootNavigator = (signedIn = false) => {
const commonNavigationOptions = {
headerStyle: {
shadowColor: 'transparent',
elevation: 0
},
headerTintColor: DEFAULT_THEME.topaz
};
const SignedIn = createStackNavigator(
{
Home: {
screen: Drawer('Home'),
navigationOptions: () => ({
headerStyle: {
height: 0
},
header: getSafeArea(DEFAULT_THEME.backgrouncolorHomeSafeArea)
})
},
Cards: {
screen: Tabs('Cards'),
navigationOptions: () => ({
headerStyle: {
height: 0
}
})
},
);
const SignedOut = createStackNavigator(
{
SignIn: {
screen: LoginContainer,
navigationOptions: () => ({
headerStyle: {
height: 0
},
header: getSafeArea(DEFAULT_THEME.dark)
})
},
SelectableCardsList: { screen: SelectableCardsListComponent },
);
return createSwitchNavigator(
{
SignedIn: { screen: SignedIn },
SignedOut: { screen: SignedOut }
},
{
initialRouteName: signedIn ? 'SignedIn' : 'SignedOut'
}
);
};
const Drawer = (initialRoute) => createDrawerNavigator(
{
Home: { screen: Tabs('Home') },
{
initialRouteName: initialRoute,
contentComponent: CustomDrawerComponent
}
);
const Tabs = (initialRouteName) => createBottomTabNavigator(
{
Home: {
screen: HomeContainer,
navigationOptions: {
tabBarLabel: I18n.t('tabs.me')
}
},
Home2: {
screen: Home2,
navigationOptions: {
tabBarLabel: I18n.t('tabs.credentials')
}
},
{
initialRouteName: initialRouteName,
tabBarComponent: ({ navigation }) => <CustomBottomBarComponent navigation={navigation} navigationState={navigation['state']} />,
tabBarOptions: {
style: {
backgroundColor: 'white'
}
}
}
);
try this solution with react-navigation V3 and send me an error
I try the following:
encapsulate createSwitchNavigator in createAppContainer and separate (SignedOut and SignedOut) createStackNavigator out of createSwitchNavigator, the rest is still the same.
export const createRootNavigator = (signedIn = false) => createAppContainer(createSwitchNavigator(
{
SignedIn: { screen: SignedIn },
SignedOut: { screen: SignedOut }
},
{
initialRouteName: signedIn ? 'SignedIn' : 'SignedOut'
}
));
I get the following error: The component for route 'Home' must be a react component. For Example
import MyScreen from './MyScreen';
...
Home : MyScreen,
}
you can also use a navigator:
The problem is located in this part:
const SignedIn = createStackNavigator(
{
Home: {
screen: Drawer,
Also try to change Drawer for any component (a component with a blank screen) and this works, but I can not insert the Tabs in the Drawer.
Thank you very much for your help.

Modal navigation for certain screens in react-native app

For 3 screens in my app I need to make a modal transition.
For the rest of the screens - I need the default right-to-left transition.
How do I achieve this with different stack navigators?
const AuthStack = createStackNavigator(
{
LogIn: { screen: LogInScreen },
Signup: { screen: SingupScreen },
Welcome: { screen: WelcomeScreen },
CodeVerification: { screen: CodeVerificationScreen },
PasswordSelection: { screen: PasswordSelectionScreen },
RegistrationSuccess: { screen: RegistrationSuccessScreen }
},
{
initialRouteName: 'LogIn'
}
)
const ModalNavigator = createStackNavigator({
ContactInfoEdit: { screen: ContactInfoEditScreen },
DeliveryAddressEdit: { screen: DeliveryAddressEditScreen },
OrderPlacedScreen: { screen: OrderPlacedScreen },
},
{
initialRouteName: 'ContactInfoEdit',
})
const ProductsStack = createStackNavigator(
{
Products: {
screen: ProductsScreen
},
Product: {
screen: ProductScreen
},
ProductBuy: {
screen: ProductBuyScreen
},
OrderConfirm: {
screen: OrderConfirmScreen
},
PlaceOrder: {
screen: PlaceOrderScreen
},
Modal: ModalNavigator
},
{
initialRouteName: 'Products',
mode: 'modal',
}
)
If I set mode: modal it will make all the navigation animations will be modal.
If I remove it, all the navigations will be default (right-to-left)
const ProductsTabStack = createBottomTabNavigator(
{
Orders: { screen: OrdersScreen },
Products: { screen: ProductsStack },
Profile: { screen: ProfileScreen }
},
{
initialRouteName: 'Products',
backBehavior: 'none',
tabBarOptions: {
activeTintColor: '#ffffff',
inactiveTintColor: primaryColor,
activeBackgroundColor: primaryColor,
labelStyle: {
marginBottom: 17,
fontSize: 15,
},
tabStyle: {
shadowColor: primaryColor,
borderWidth: 0.5,
borderColor: primaryColor,
},
},
},
)
export const AppNavigator = createSwitchNavigator({
Auth: AuthStack,
Categories: ProductsTabStack
})
I tried setting mode: modal in the ModalNavigator, but then it took the default parent navigation.
You probably want to try to use StackNavigatorConfig while navigating to that screen this.props.navigation.navigate('ScreenName', params, {mode: 'modal'})
If you want to keep all your transition code in same file as you have right now, you can do the same as what react-navigation is suggesting here
It goes something like that
import { createStackNavigator, StackViewTransitionConfigs } from 'react- navigation';
/* The screens you add to IOS_MODAL_ROUTES will have the modal transition. */
const IOS_MODAL_ROUTES = ['OptionsScreen'];
let dynamicModalTransition = (transitionProps, prevTransitionProps) => {
const isModal = IOS_MODAL_ROUTES.some(
screenName =>
screenName === transitionProps.scene.route.routeName ||
(prevTransitionProps && screenName ===
prevTransitionProps.scene.route.routeName)
)
return StackViewTransitionConfigs.defaultTransitionConfig(
transitionProps,
prevTransitionProps,
isModal
);
};
const HomeStack = createStackNavigator(
{ DetailScreen, HomeScreen, OptionsScreen },
{ initialRouteName: 'HomeScreen', transitionConfig: dynamicModalTransition }
);
OK, found a workaround for custom transitions in 1 StackNavigator using https://www.npmjs.com/package/react-navigation-transitions:
const handleCustomTransition = ({ scenes }) => {
const nextScene = scenes[scenes.length - 1]
if (nextScene.route.routeName === 'ContactInfoEdit')
return fromBottom()
else
return fromRight()
}
const ProductsStack = createStackNavigator(
{
Products: {
screen: ProductsScreen
},
Product: {
screen: ProductScreen
},
ProductBuy: {
screen: ProductBuyScreen
},
OrderConfirm: {
screen: OrderConfirmScreen
},
PlaceOrder: {
screen: PlaceOrderScreen
},
ContactInfoEdit: { screen: ContactInfoEditScreen },
DeliveryAddressEdit: { screen: DeliveryAddressEditScreen },
OrderPlacedScreen: { screen: OrderPlacedScreen },
},
{
initialRouteName: 'Products',
transitionConfig: (nav) => handleCustomTransition(nav)
}
)

Custom header in nested TabNavigator

I have a fairly complication navigation flow requirement for an app I'm working on.
I have a bottom tab bar, for each tab I'll be having a top tab bar for additional related views.
Which I have working, however on the videos tab in the nested "All" tab, I need to add a search bar to the header, and on the "Favourites" tab I'll be having yet another custom header with an "Edit" button at the top right.
How can I achieve this navigation whilst allowing React Navigation to co-ordinate everything. See images below:
What I don't want to do is disable the header at the MainNavigator level and enable it for particular routes. Or even worse embed the header and the tab bar on individual containers.
routes.js
import {
StackNavigator,
TabNavigator,
DrawerNavigator
} from "react-navigation";
import Feed from "./containers/Feed";
import Auth from "./containers/Auth";
import News from "./containers/News";
import Videos from "./containers/Videos";
import FavouriteVideos from "./containers/FavouriteVideos";
const DashboardNavigator = TabNavigator(
{
Feed: {
screen: Feed
},
News: {
screen: News
}
},
{
tabBarPosition: "top"
}
);
const VideoNavigator = TabNavigator(
{
Videos: {
screen: Videos,
navigationOptions: {
title: "All"
}
},
Favourites: {
screen: FavouriteVideos
}
},
{
tabBarPosition: "top"
}
);
const MainNavigator = TabNavigator(
{
Dashboard: {
screen: DashboardNavigator,
navigationOptions: ({}) => ({
title: "Dashboard"
})
},
Video: {
screen: VideoNavigator,
navigationOptions: ({}) => ({
title: "Videos"
})
}
},
{
swipeEnabled: false,
animationEnabled: false,
tabBarPosition: "bottom"
}
);
const AuthenticatedNavigator = DrawerNavigator({
App: {
screen: MainNavigator
}
});
const RootNavigator = StackNavigator({
LoggedOut: {
screen: Auth
},
Authenticated: {
screen: AuthenticatedNavigator
}
});
export default RootNavigator;
Snack
https://snack.expo.io/H1qeJrLiM
Images
You can use react-navigation addListener function with combination setParams to achieve desired behavior.
You can listen for focus and blur events and then change a parameter. Then in your route config you can look for this parameter and decide what to render for header. I changed your snack to show a working example of what I am suggesting.
Example
const MainNavigator = TabNavigator(
{
Dashboard: {
screen: DashboardNavigator,
navigationOptions: ({}) => ({
title: "Dashboard"
})
},
Video: {
screen: VideoNavigator,
navigationOptions: ({navigation}) => {
let title = 'Videos';
navigation.state.routes.forEach((route) => {
if(route.routeName === 'Videos' && route.params) {
title = route.params.title;
}
});
// I set title here but you can set a custom Header component
return {
tabBarLabel: 'Video',
title
}
}
}
},
{
swipeEnabled: false,
animationEnabled: false,
tabBarPosition: "bottom"
}
);
export default class Videos extends Component {
constructor(props) {
super(props);
this.willFocusSubscription = props.navigation.addListener(
'willFocus',
payload => {
this.props.navigation.setParams({title: 'All Videos'});
}
);
this.willBlurSubscription = props.navigation.addListener(
'willBlur',
payload => {
this.props.navigation.setParams({title: 'Just Videos'});
}
);
}
componentWillUnmount() {
this.willFocusSubscription.remove();
this.willBlurSubscription.remove();
}
render() {
return (
<View>
<Text> Videos </Text>
</View>
);
}
}

Perspective Animation Drawer with React Native?

I want to create perspective animation like following -
I am using react-native-scaling-drawer & have currently done -
My App.js is the root file which is as follows -
App.js
const AppNavigator = StackNavigator(
{
walkthroughStack: {
screen: WalkthroughStack,
},
drawerStack: {
screen: DrawerStack,
},
},
{
initialRouteName: 'walkthroughStack',
headerMode: 'none',
},
);
export default AppNavigator;
My Walkthrough.js file is the file which shows the Walkthrough of the app & is as follows -
WalkthroughStack.js
const WalkthroughStack = StackNavigator(
{
Walkthrough: {
screen: Walkthrough,
},
},
{
headerMode: 'none',
navigationOptions: {
headerVisible: false,
},
initialRouteName: 'Walkthrough',
},
);
export default WalkthroughStack;
My DrawerStack.js is the file which has the animation shown in the repo -
DrawerStack.js
let defaultScalingDrawerConfig = {
scalingFactor: 0.6,
minimizeFactor: 0.6,
swipeOffset: 20
};
class CustomDrawerView extends Component {
constructor(props) {
super(props);
}
componentWillReceiveProps(nextProps) {
/** Active Drawer Swipe **/
if (nextProps.navigation.state.index === 0)
this._drawer.blockSwipeAbleDrawer(false);
if (nextProps.navigation.state.index === 0 && this.props.navigation.state.index === 0) {
this._drawer.blockSwipeAbleDrawer(false);
this._drawer.close();
}
/** Block Drawer Swipe **/
if (nextProps.navigation.state.index > 0) {
this._drawer.blockSwipeAbleDrawer(true);
}
}
setDynamicDrawerValue = (type, value) => {
defaultScalingDrawerConfig[type] = value;
/** forceUpdate show drawer dynamic scaling example **/
this.forceUpdate();
};
render() {
const {routes, index} = this.props.navigation.state;
const ActiveScreen = this.props.router.getComponentForState(this.props.navigation.state);
return (
<ScalingDrawer
ref={ref => this._drawer = ref}
content={<LeftMenu navigation={this.props.navigation}/>}
{...defaultScalingDrawerConfig}
onClose={() => console.log('close')}
onOpen={() => console.log('open')}
>
<ActiveScreen
navigation={addNavigationHelpers({
...this.props.navigation,
state: routes[index],
openDrawer: () => this._drawer.open(),
})}
dynamicDrawerValue={ (type, val) => this.setDynamicDrawerValue(type, val) }
/>
</ScalingDrawer>
)
}
}
const AppNavigator = StackRouter({
{
Main: {
screen: Main,
},
Walkthrough: {
screen: Walkthrough,
},
Typography: {
screen: Typography,
},
},
{
headerMode: 'none',
gesturesEnabled: false,
navigationOptions: {
headerVisible: false,
},
initialRouteName: 'Main',
},
);
const CustomDrawer = createNavigationContainer(createNavigator(AppNavigator)(CustomDrawerView));
export default CustomDrawer;
But this isn't working as shown in the README of the repo. How to do it ?
Example given works with React-Navigation's v1. Make sure you are using v1 for react-navigation