Rendering a TabNavigator in Storybook - react-native

I want to use Storybook.js to mock up a page that utilizes createMaterialTopTabNavigator(). However I can't render the material Tab because the output of the function is not actually a react component. So when I directly tried <TabNavigator /> it threw an invariant violation that "navigation" was missing. The only work-around I found was using createAppContainer() around the TabNavigator. But I know that it is bad practice to use more than one navigator. Is there any way to render a react navigator element without needing the navigation container?
This is my current working code for the storybook:
const tabConfiguration = {
Old: {
screen: OldProductsScreen,
navigationOptions: {
title: "Old"
}
},
New: {
screen: NewProductsScreen,
navigationOptions: {
title: "New"
}
}
};
const TabNavigator = createMaterialTopTabNavigator(tabConfiguration);
storiesOf('Test', module)
.add('Initial', () => {
const TAB = createAppContainer(TabNavigator);
return (
<TAB / >
);
})
;

I know that it is bad practice to use more than one navigator. Is there any way to render a react navigator element without needing the navigation container?
It's bad practice to do that in an app. But since you're using Storybook where each container is in like its own independent app, this is totally fine.

Related

React Navigation(v2) How to pass props to my TabNavigator and send those props to a screen

I am new to React-Native and React. I am trying to implement a simple Tab Navigation on my mobile app using React Navigation (v2)
Basically there are two files:
Router.js
// imports...
const Tabs = TabNavigator(
{
Logs: {
screen: Logs,
// here I would like to recieve the jwt and pass it as a param to the logs screen
},
NewRide: {
screen: NewRide
}
},
{navigationOptions: ...}
}
export default tabs;
LoggedIn.js
export default class LoggedIn extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View>
<Tabs jwt={this.props.jwt} /> // here I pass the jwt as a prop
</View>
);
}
What I am trying to do is pass a prop named jwt from my LoggedIn class located in LoggedIn.js to Tabs located in Router.js . From the Tabs call I would ultimately like to send the Jwt received as a prop to the Logs screen.
Im not sure how to to recieve the Jwt in the Router.js file and pass it to the logs screen. Any help regarding this would be hugely appriciated as i have been stuck on this for a good two days. Kind regards Matt.
You can pass a global parameter to the navigation which can be available in every screen for that navigation.
screenProps - Pass down extra options to child screens
Sample
const SomeTab = TabNavigator({
// config
});
<SomeTab
screenProps={/* this prop will get passed to the screen components as this.props.screenProps */}
/>

Pass different parameter to each screen when I use TabNavigator (react-navigation)

I'm making app with using React Native and react-navigation.
I'm looking for a way to pass different parameter to each screen. Each screen is same class.
There should be a way to archive it but I couldn't find so far,
except declaring two different wrapper class each and pass screenProps, but it seems insane.
const Tab = TabNavigator(
{
main:{screen:TodoList}, //pass "abc" for this screen
main2:{screen:TodoList} //pass "def" for this screen
}
Use the other signature if you have custom props :
const Tab = TabNavigator(
{
main:{ screen: (props) => <TodoList {...props } myProp="abc" />}, //pass "abc" for this screen
main2:{ screen: (props) => <TodoList {...props } myProp="def" />} //pass "def" for this screen
}
If you have shared "prop" common among all screens, you can pass it via screenProps when initiating the Tab itslef.
<Tab screenProps={{myCommonProp: "ghijkl"}} />

React native: How to navigate nested navigator (wrapped by a component)

I read on the docs about navigating nested navigator https://reactnavigation.org/docs/en/navigation-actions.html#setparams
const navigateAction = NavigationActions.navigate({
routeName: 'Profile',
params: {},
action: NavigationActions.navigate({ routeName: 'SubProfileRoute'})
})
this.props.navigation.dispatch(navigateAction)
However, my nested navigator SubProfileRoute is wrapped by a component
module.exports = class _ extends React.Component { ... }
not
module.exports = StackNavigator({})
So how do i do it?
You'll need to assign the router of the child navigator to the component wrapping it - that way the outer navigator will pass the navigation property to the child navigator.
See explicitly rendering more than one navigator.
So, I managed to solve this problem on my side. The issue is the links shared here don't work, even though Kraylog's answer provides a good place to start the research.
I found that, in order for nested routers to work, the navigation does indeed look for a child router in the component that it renders. However, it's not enough to set the router property to a child router's for everything to work with, in my example, redux navigation.
You also need to pass the navigation prop manually to the child navigator.
The documentation that helped a bit is this custom navigator guide.
The solution ended up being a simple one:
class MyNavigator extends React.Component {
static router = NestedNavigator.router;
render() {
return <NestedNavigator navigation={this.props.navigation} />;
}
}

React Navigation: Prevent multiple instances to navigate to same screen

Let's say I have this screen:
And when the user clicks on the white tooltip, it redirects to another screen. Sometimes the app lags a little bit, and clicking on the tooltip takes like ~2s to see the screen change. The problem is, during those 2s, the user taps again on this tooltip to make it happen.
And the result I get is that there are two instances of the new screen in my StackNavigator. What I mean is that I see my new screen, but when I click on "Back" I don't return to this 'Hitchhiking Map' screen, but to another instance of that same screen.
If I clicked 5 times on the callout during those 2s, then I need to click 5 times "Back" to return to the Map screen. Any way to prevent that? To put only one instance into the StackNavigator?
I am using React Navigation, more precisely a StackNavigator. Here's my code:
The "click on tooltip" part:
<MapView.Marker
onCalloutPress={() => this.props.navigation.navigate('spotDetails', { spotId: marker.id })}
/>
My screens:
const HMapNavigator = StackNavigator({
HMap: { screen: HMapViewContainer },
spotDetails: { screen: SpotDetailsViewContainer },
});
The issue of multiple navigations has been reported and there is more detail here.
react-navigation (v1.0.0-beta.7) is in beta and still being built, so until this feature is implemented, you will have to handle the debounce manually.
options
disable the button once the navigation starts
debouncing in the onPress logic or in the action if you are using redux
lodash provides a useful debounce utility, if you are looking for one.
There are many ways how to overcome double navigations.
My solution is adding a key property in navigate object:
this.props.navigation.navigate({ key: 'AnotherScreen', routeName: 'AnotherScreen', params: { ... }})}
Save if its opening to control the navigation
constructor (props) {
super(props)
this.state = {
opening: false
}
}
Create a function to control de navigation
_open (campaign) {
if (!this.state.opening) {
this.props.navigation.navigate('Campaign', {campaign})
this.setState({ opening: true })
setTimeout(() => {
this.setState({ opening: false })
}, 1000)
}
}
Call this func from your onpress
<TouchableHighlight underlayColor='transparent' onPress={this._open.bind(this, campaign)}>
In versions 1.x of React Navigation, if you specify an identifier the navigation action will be executed only once. E.g.:
navigate({ routeName: 'someScreen', key: 'someScreen', params: { someParam: 'someValue' } })
More information: https://gist.github.com/vonovak/ef72f5efe1d36742de8968ff6a708985
In versions 2.x and 3.x this is a default functionality.
More info: https://reactnavigation.org/docs/en/navigation-key.html

Custom backButtonIcon for NavigatorIOS

This question is related to
https://github.com/facebook/react-native/issues/1383
Is there anyway to have a custom back button icon / title with NavigatorIOS?
backButtonIcon, backButtonTitle is not working as expected. The left arrow is always visible. New routes with custom backButtonIcon or backButtonTitle doesnt show
this.props.navigator.push({
title: 'Next',
component: Next,
backButtonIcon: require('./../img/Back.png')
})
Use LeftButtonIcon instead of backButtonIcon like this;
this.props.navigator.push({
title: '详情',
component: PageList,
leftButtonIcon: {uri: 'nav-back'},
onLeftButtonPress: () => {
ViewController.toggleTabBar(true);
this.props.navigator.pop();
},
passProps: {
data: data
}
});
The letButtonIcon comes from Native App.
You can easily achieve this using the Navigator component. Navigator is the JS implementation of a navigator. You can customize every aspect of it and you can use it in both iOS and Android. Further more, NavigatorIos is not maintained as much as Navigator. More info here: https://facebook.github.io/react-native/docs/navigator-comparison.html#content
And here:
https://facebook.github.io/react-native/docs/navigator.html#content