react-navigation Screen that conceals TabBar from nested StackNavigator - reactjs

I'm new to react-navigation and trying to wrap my head around how to do the following:
Given this navigation structure:
RootTabNavigator
LoggedOut_StackNavigator
...
LoggedIn_StackNavigator
LoggedIn_TabNavigator <-- TabBar rendered by this Navigator
TabA_StackNavigator
ScreenA
ScreenB
I would like to be able to navigate from ScreenA to ScreenB using the typical "slide in from right" transition, in such a way that the TabBar is visible on ScreenA, but is not visible on ScreenB. In other words, when I navigate to ScreenB, I want it to take up the entire window.
Once the user transitions from ScreenA to ScreenB, they can either press the back button to return back to ScreenA, or navigate to new routes using the same transition with the TabBar still not visible.
What I've tried:
navigationOptions.tabBarVisible: this property only seems to work when applied to TabA_StackNavigator itself, which means that all of the screens in its stack also conceal the TabBar. Adding it to the screens inside the StackNavigator has no effect.
Adding a new AllScreens_StackNavigator as a sibling of LoggedIn_TabNavigator and navigating to routes inside this navigator, I get the error: Expect nav state to have routes and index, {"routeName":"ScreenB", "params": {}, "key": "init-id-1516..."}. The navigation action I dispatched to try to do this:
{
"action": Object {
"params": Object {},
"routeName": "ScreenB",
"type": "Navigation/NAVIGATE",
},
"params": Object {},
"routeName": "AllScreens_StackNavigator",
"type": "Navigation/NAVIGATE",
}
Any help is greatly appreciated!

Edit: this answer is relevant to react-nagivation v1.~ (pre v2.0)
As suggested in the comments, see this issue:
https://github.com/react-navigation/react-navigation-tabs/issues/19
Apparently, the navigationOptions of an inner component affect the containing navigator's parent navigator as well.
Solution
That means this code should work for you:
class ScreenB extends React.Component {
static navigationOptions = {
header: () => null, //this will hide the Stack navigator's header (TabA_StackNavigator)
tabBarVisible: false //this will hide the TabBar navigator's header (LoggedIn_TabNavigator)
}
Explanation
First, you can set the navigation options per individual screen (component). You can see how in the code snippet above or here: React Navigation - Screen Navigation Options
Second, you tried:
Adding it to the screens inside the StackNavigator has no effect.
It didn't work because hiding the StackNavigator's header requires setting the header field to null.
From the React Navigation documentation:
React Element or a function that given HeaderProps returns a React
Element, to display as a header. Setting to null hides header
Third, using tabBarVisible is actually correct, but it affects only the TabNavigator. And to make it disappear only for one tab and not for all the tabs, you need to set it on the specific screen. ScreenB in your case.
Hope this helps!

The following is what ended up working for me, so I'm posting it the hopes that it helps others. I haven't had a chance to try #talzaj's implementation so I'll leave it up to others to upvote whatever works best for them. The following solution has been working well for me, including inside nested navigators.
I updated my navigation structure such that:
LoggedIn_StackNavigator still has LoggedIn_TabNavigator as one of its screens, and this LoggedIn_TabNavigator is the initial route of LoggedIn_StackNavigator as set using initialRouteName.
LoggedIn_StackNavigator also contains a route for every screen that will ever need to be shown full screen and conceal the tab bar. (If you are re-using screens, where some are shown with the tab bar visible and others where it is not, make sure to use unique keys for routes that re-use the same screen.
Navigation Structure
So, the navigation structure looks like:
RootTabNavigator
LoggedOut_StackNavigator
LoggedIn_StackNavigator
ScreenA // ( reuse screen component, different route key )
ScreenB // ( reuse screen component, different route key )
LoggedIn_TabNavigator <-- TabBar rendered by this Navigator
TabA_StackNavigator
ScreenA
ScreenB
LoggedIn_StackNavigator:
And LoggedIn_StackNavigator looks like:
import { StackNavigator } from 'react-navigation';
import LoggedIn_TabNavigator from './LoggedIn_TabNavigator';
import {
ScreenA,
ScreenB,
} from './LoggedIn_TabNavigator/TabA_StackNavigator/Screens';
const LoggedIn_StackNavigator = StackNavigator({
WithoutTabBar_ScreenA: {
screen: ScreenA
},
WithoutTabBar_ScreenB: {
screen: ScreenB
},
LoggedIn_TabNavigator: {
screen: LoggedIn_TabNavigator
}
}, {
initialRouteName: 'LoggedIn_TabNavigator'
});
export default LoggedIn_StackNavigator;
From there, I wrote a helper HOC for pushing full screen routes:
import React from 'react';
import { withNavigation } from 'react-navigation';
import { fullScreenRoutePrefix } from './somewhere';
export default function withNavigateFullScreen(Child) {
#withNavigation
class WithNavigateFullScreenHOC extends React.Component {
navigateToFullScreenRoute = (routeName, params) => {
this.props.navigation.navigate(
`${fullScreenRoutePrefix}${routeName}`, params
);
}
render() {
return (
<Child
{...this.props}
navigateFullScreen={this.navigateToFullScreenRoute}
/>
);
}
}
return WithNavigateFullScreenHOC;
}
And then I can navigate to full screen routes like so:
import React from 'react';
import { withNavigateFullScreen } from 'components/higher-order';
import { Text } from 'react-native';
#withNavigateFullScreen
export default class ScreenA extends React.Component {
goToScreenB = () => {
this.props.navigateFullScreen('ScreenB');
}
render() {
return <Text onPress={this.goToScreenB}>Go To Screen B</Text>;
}
}

Related

React redirect on click of svg element

I have a single page React App that is d3 and SVG heavy, and I would like to be able to redirect from one page to another when a user clicks on an svg rect on one of my pages. I am familiar with this.props.history.push() as well as the <Link> component from the react-router-dom library, however neither of these seem to help in this instance.
The svg element of relevance here is deep in a graphing component of mine that is 3-4 children down from the front-end's main App.js file that does all of the routing, and when I run console.log(this.props) in my component with the svg, there is no history object on the props. I'm not sure if a reproducible example is needed here, as I just need direction.
In short, I have no idea what should go into the on-click function that is associated with my svg rect, to enable redirect in my app. Any thoughts on this would be greatly appreciated!
Edit: obviously this is wrong but i tried to return a Redirect component in on-click handler and it didn't work:
...
...
function handleMouseClick() {
console.log('clicked')
return <Redirect to='/stats' />;
}
myRect.on('click', handleMouseClick)
...
Edit2: should i put the rect elements inside of components in the svg? is that even possible?
You can add the history prop from react-router to a component by wrapping it with withRouter. Just make sure whatever is mounting your component is using the wrapped version (usually by only exporting the wrapped component).
import React from 'react';
import { withRouter } from 'react-router';
class MyComponent extends React.Component {
render() {
return (
<button onClick={() => this.props.history.push('/newpage')}>
Click me
</button>
);
}
}
export default withRouter(MyComponent);

How can I navigate into components on react native?

I've fletched a list of items and rendered it into my app.
Its the first page of the app.
The thing that I want to do is:
Make each of the items "touchable", and when you touch it, you open a component filled with objects from a second fetch requisition.
I am new to react native, do you know if I have to use a lib or something to do it?
I'll try to answer your questions one by one.
Make each of the items "touchable"
Wrap your components with TouchableOpacity which you can import from react native like this import {TouchableOpacity} from "react-native";
when you touch it, you open a component filled with objects
You need to implement onPress method there and also react navigation to load other components.
<TouchableOpacity onPress={() => this.props.navigation.navigate("newScreenName")}>
<MyCustomComponent>
...
</MyCustomComponent>
</TouchableOpacity>
and creating screen will be like this :
import { createStackNavigator } from "react-navigation";
import Screen1 from "./Screen1";
import Screen2 from "./Screen2";
...
const customStackNavigator = createStackNavigator(
{
newScreenName: {
screen: Screen1
},
newScreenName1: {
screen: Screen2
}
},
{}
);
check API & Docs here
Also, Please check this example for more details

Next.js - route based modal

When using Next.js, I want to show a modal based on a url, on top of another page.
If gallery.js is the page component, I want /gallery/image/1232132 to display a modal with an image, on top of the gallery page.
Is that possible?
This question is a bit old, but since March 2020 there's a full example on the official Next.js repo (you should probably use this since it must be the "recommended way" by the maintainers):
https://github.com/vercel/next.js/tree/canary/examples/with-route-as-modal
Here's the original issue:
https://github.com/vercel/next.js/issues/8023
And the related PR:
https://github.com/vercel/next.js/pull/11473
If I understand your question correctly you want to add deep links to the individual gallery items. This is possible, but you need a custom server to handle custom routes.
The first thing you need to do is setup the routes. I shared an example here: using React router with Next JS route.
const nextRoutes = require('next-routes');
const routes = (module.exports = nextRoutes());
routes
.add('gallery', '/gallery')
.add('gallery-item', '/gallery/image/:image', 'gallery')
Then you can access this parameter in the getInitialProps method, and render the modal if the image parameter is set:
import React from 'react';
import PropTypes from 'prop-types';
export default class Gallery extends React.Component {
static propTypes = {
image: PropTypes.string
};
static getInitialProps({query: {image}}) {
return {image};
}
render() {
const {image} = this.props;
return (
<div>
{image &&
// render modal
}
// render gallery
</div>
);
}
}

React Navigation trigger when routeName changed

I'm using react navigation for navigate screen. I used nested navigation, TabNavigator and StackNavigator. How can I know in current screen has been swipe / change to other screen and change the state inside the screen without using Redux . I try to get this.props.navigation.state.routeName , it get the routeName of the screen, but how to trigger when routeName has change
const mainNav = TabNavigator({
Home: {
screen: HomeScreen,
},
Live: {
screen: LiveScreen,
},
Radio: {
screen: RadioScreen,
},
} );
export const mainStack = StackNavigator({
Home: { screen: mainNav},
Content: { screen: ContentScreen },
});
At present there is no way to track swipe gestures in react navigation. You can use hacks to achieve this, But mostly all of them requires hooking react navigation with redux.
Best way to track your screens will be to use react redux and hook the state with appState. This will give you most control over navigation and you can also use redux actions to control navigations along with other business stuff f in the app.
Simplest method to use react navigation with redux
You can refer
https://github.com/react-community/react-navigation/issues/1063

referencing constants and variables across separate class files

My App.js file was getting very large, so I decided to move out all my classes to their own separate file, and this is what remains in my App.js
import React from 'react';
import {
AppRegistry,
Text,
View,
Button,
Image
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import { TabNavigator } from 'react-navigation';
//screen imports
import RecentChatScreen from './RecentChatScreen';
import AllContactsScreen from './AllContactsScreen';
import ChatScreen from './ChatScreen';
import HomeScreen from './HomeScreen';
import InfoScreen from './InfoScreen';
const MainScreenNavigator = TabNavigator({
Home: { screen: HomeScreen },
Recent: { screen: RecentChatsScreen },
All: { screen: AllContactsScreen },
});
const NavTest = StackNavigator({
Home: { screen: MainScreenNavigator },
Chat: { screen: ChatScreen },
Info: { screen: InfoScreen }
});
I think that looks fine.
However in some of the class files, I need to reference those other screens in button events, like this:
onPress={ () => navigate('Home')}
This worked fine before when everything was in App.js, but how would I reference these screens(Home, Chat, Info, Recent, All) now in the their separate files when the definitions are in App.js?
Thanks!
You can export them in App.js:
// App.js
// ...
export {
MainScreenNavigator,
NavTest,
}
And then import them from it:
import {MainScreenNavigator, NavTest} from './path/to/App.js';
If your intent is just to navigate to the different screens, and not use any properties defined within a particular screens class, then you would not need to import anything. When you define the StackNavigator and lets say you are passing in that value into the AppRegistry as the entry point of the application. In your case, it could be something like this:
AppRegistry.registerComponent('NavTest', () => NavTest)
Now within any of the screens, the navigatation object is passed in as a prop. You can then use that to navigate to any of the defined screens. The initial setup of the StackNavigator is essentially a mapping from a routeName to a react component (a class). So when you want to navigate to a particular class, all you need is the name of the route not the class or component it represents, the navigator already has all that. This name would be the key passed into the StackNavigator for a particular screen. In your example, the routeNames are Home, Chat and Info. So just doing something like this would work:
const { navigate } = this.props.navigation;
return (
<View>
<Text>Navigate Test</Text>
<Button onPress={() => navigate('Chat')} title="Go to chat"/>
</View>
);

Resources