How to hide TabNavigator dynamically on React Native - react-native

So i have an app with react native navigator, what I plan for my app is to show a Tutorial when the user first launches the app, I use react-copilot for it, it works really great, but the problem is, React copilot takes time to initiate, and it launches BEFORE the react-navigator.
The problem is that the user can click the navigator thus breaking the tutorial or even crashing the system because the tutorial did not initiate properly.
I plan to make the navigator to be disabled dynamically when the tutorial not yet started. Here's the snippet of the code from the navigationOptions on the appNavigation
TabMenu.navigationOptions = ({ navigation, screenProps }) => {
const childOptions = getActiveChildNavigationOptions(navigation, screenProps);
return {
title: childOptions.title,
tabBarVisible: childOptions.tabBarVisible,
header: null
};
};
and here's the static value on the component
static navigationOptions = {
tabBarVisible: false
}
It works, but the problem is when the tutorial ends and I set the static value to true, the tabBar doesn't appear. Is there any way around this?
Thank you in advance
EDIT :
i need to clarify that what i need is to make the tabbar appear and dissapear within the same page after certain activity (in this case tutorial) finished without the need to reload/navigate to the same page

It's like Gabriel answer
static navigationOptions = ({ navigation, screenProps }) => {
const { tabBarVisible = true } = navigation.state.params
? navigation.state.params
: {};
return {
tabBarVisible: tabBarVisible
};
};
Place the navigation options inside any Tab Item and update the tabBarVisible property like this.
this.props.navigation.setParams({
tabBarVisible: false
});

I would try putting all the tutorial from react-copilot into a different page which is not inside the bottom navigation bar or maybe even in a Modal (which by default cover the whole application).
After the react-copilot instructions are done you are free to navigate to the bottom navigation bar or dismiss the Modal.
NEW SUGGESTION AFTER COMMENT:
I think you could change some values in the navigationOptions by doing the following:
static navigationOptions = ({ navigation }) => {
return {
headerTitle: navigation.getParam('title', ''),
}
};
and then in a function inside the component calling the following:
this.props.navigation.setParams({ "title": 'brand new name') })
This works for me on an app where I had to change the header title of a page after a button was clicked. But I'm not sure if that would work with the attribute tabBarVisible. Would you mind giving it a try?

Related

Does anyone know how to match a ProfileScreen image with a Drawer Navigation image? React Native

I have created a navigation drawer that has an image and a profile screen that has an image picker, however my images need to be manually loaded so they look the same. How can I upload my profile screen image and my navigation drawer image becomes updated automatically? I would appreciate any help (videos, links, share code etc.) Thank you,
For your issue you can do something like this
async componentWillReceiveProps(props) {
let user_id = await getuserinfo(this)
if (user_id) {
this.setState({
user_id: user_id
}, () => {
this.getUserDetails()
})
} else {
this.props.navigation.navigate('login')
}
}
whenever the props has changed you can update the drawer details by calling the data in the drawer.

How to hide tab bar for a specific scene in React Native (react-native-router-flux)?

I am trying to hide a whole tab bar for the specific page on my app.
I have tried to hide it on the routing and inside the container too.
But, it did not work. I could hide header navbar in both scenarios but it is not working
for tabBar.
Below are my attempt codes:
<Scene
key="showBarcodeScanner"
hideNavBar
hideTabBar
{...DefaultProps.navbarProps}
iosStatusbar="light-content"
component={BarcodeScan}
/>
Below method did not work either
static navigationOptions = ({ navigation }) => ({
header: null,
tabBarVisible: false
});
I have checked the source and there is a logic to hide the tab (did not go deep though).
if (navigationParams.hideTabBar != null) {
if (navigationParams.hideTabBar) {
res.tabBarVisible = false;
}
} else if (hideTabBar) {
res.tabBarVisible = false;
}
Am I missing something? Is there any other method to hide the tabbar for a specific page?
See Readme of React Native Router.
Actions.refresh({key: 'showBarcodeScanner', hideNavBar: true, hideTabBar: true});
Hope it help, u can put any PARAMS you want to refresh.
I found the solution after some research.
The Scene I wanted to hide was inside the Stack which was under the Tabs in my routes.
And without shuffling navigators below code does not work based on official source:
React Navigation
tabBarVisible: false
I just created a new Stack above my Tabs and the problem is solved.

Reactnavigation with parameters

I am using React navigation I am looking for a way to pass parameters into my navigation stack as follows. I have actually one screen which I want to use X amount of times. It only needs an url and a title, and based on the url it should do exactly the same for each url.
So i want to create an object like so:
const urls = {
{title: 'foo', url: 'https://someurl'},
{title: 'bar', url: 'https://someotherurl'}
}
And now in my Navigation component I would like to do something like:
export default createMaterialTopTabNavigator({
SomeKey: {
// Loop here over the urls and create a component and pass props.
}
});
My issue is that I can't find in the documentation how to pass the title and url parameter via the navigator to the specific screens.
Any Suggestions?
I can help you with one part, you can pass variables from Navigator like this
export default createMaterialTopTabNavigator({
ScreenOne: {
screen:props=> <ScreenOne {...props} screenProps={yourpropsr}/>
}
});
The documentation has example for StackNavigator but I hope this will work for TabNavigator too. Documentation link here
I'm doing something similar in my app, but I'm grabbing my array from an API and build my navigation upon that. For each item in my array, I build the same screen setup in a tab navigation and have them all available from a drawer navigator.
You could do something like this:
let NavigatorConfig = {};
urls.forEach(item => {
NavigatorConfig = {
...NavigatorConfig,
[item.title]: {
screen: props => <MyComponent {...props} url={item.url} />
}
};
});
export default createMaterialTopTabNavigator(NavigatorConfig);

React navigation state management without redux

I'm using React Navigation library for my React Native project and struggling to understand how to handle state with it.
In normal React Native application I can have state at the top level component and pop events from child components via props, however with React Navigation it seems that I cannot pass any props to components used as Screens.
After reading through related GitHub issue it seems that library devs are very opinionated in forcing everyone to use some kind of global event handler - redux or mobx, I guess.
The handler which needs to modify the following state. I got stuck when I started to try to move the state inside the app as I couldn't figure out how to:
Pass the handler to the TaskForm component.
Pass the state as props to TaskList if its rendered as part of App.js
Please, avoid replying "just use redux". I believe that using redux in this example would be massive overkill.
I use react native and react navigation in my app without redux, and so far it’s working great. The trick is passing screenProps all the way down the line.
For example, I have a More view. I create a basic More view with 2 sub views in a stack:
class More extends Component {
render() {
return <something>
}
}
class SubView1 extends Component {...}
class SubView2 extends Component {...}
Then I create the stack:
const MoreStack = StackNavigator({
More: {
screen: More
},
SubView1: {
screen: SubView1,
},
...
}, options);
And then I create a wrapper class that I export:
export default class MoreExport extends Component {
static navigationOptions = {
title: "More"
}
render() {
return <MoreStack screenProps={this.props.screenProps} />;
}
}
If all of this is in More.js, I can just import More from More.js and use it anywhere else.
If I then pass in screenProps to my root view and then have a wrapper class for each layer, I can pass the screenProps all the way down, and all views can access them using this.props.screenProps.
I use a wrapper like the one above around each StackNavigator and TabNavigator, and the screenProps are passed all the way down.
For example, in my root class’s render method, I could do:
return <More screenProps={{prop1: something, prop2: somethingElse}} />
And then the More class and each SubView in the MoreStack would all have access to these props.
Feel free to let me know if you want more clarification!
Disclaimer: I don’t know if this is the correct or recommended way to do it, but it does work
You can set param to navigation like this:
static navigationOptions = ({ navigation }) => {
return {
tabBarIcon: ({ tintColor, focused }) =>
<View>
<Icon name="bell-ring" size={24} color={focused ? 'green' : 'black'} />
{(navigation.state.params.badgeCount && navigation.state.params.badgeCount > 0) ?
<Text>{navigation.state.params.badgeCount}</Text>
:
<View></View>
}
</View>
}
}
and change badgeCount by:
this.props.navigation.setParams({ badgeCount: 3 })
After being inspired by steffeydev I looked more around react community issues and found a very simple example of wrapper using function.
It's surprisingly simple solution and I don't know why I didn't think about it before.
The function is the following:
const createComponent = (instance, props) =>
navProps => React.createElement(instance, Object.assign({}, props, navProps));
Thanks for inspiration and pointing me to screenProps which lead me to finding this solution.
I was struggling with the same issue and tried some of the other answers before discovering the following part of the documentation for React Navigation: https://reactnavigation.org/docs/en/stack-navigator.html#params.
Essentially, each Screen in the Stack can be passed params which can include handlers and then the various screens can interact with the application state.
My general structure is then to have an App class with state and handlers and the handlers are then passed into each Navigation Screen as needed. I'm not sure if I have this pattern right, but it's the way I understood the general React tutorial.
Example: In my demo app, I have a page flow like this:
Park finder screen -> Park detail screen (with Bookmark action)
Bookmark list screen -> Park detail screen
If you find a park, you can click on a Bookmark button which adds the park to the list of bookmarks shown on the Bookmark screen. You can then click on a park bookmark to see the details.
My App looks generally like this:
import React, { Component } from 'react';
// Screens
import ParkFinderScreen from './Components/ParkFinderScreen';
import ParkBookmarksScreen from './Components/ParkBookmarksScreen';
import ParkDetailsScreen from './Components/ParkDetailsScreen';
// Navigation
import { createStackNavigator, createBottomTabNavigator, createAppContainer } from 'react-navigation';
class App extends Component {
constructor(props) {
super(props);
this.state = {
bookmarks: new Map()
};
}
// Bookmark the park shown in the detail section.
handleBookmark (park) {
let newBookmarks = this.state.bookmarks;
newBookmarks.set(park.parkCode, park);
this.setState({
bookmarks: newBookmarks
});
}
render() {
const FinderStack = createStackNavigator(
{
ParkFinder: {
screen: ParkFinderScreen
},
ParkFinderDetails: {
screen: ParkDetailsScreen,
params: {
handleBookmark: (park) => this.handleBookmark(park),
}
},
}
);
const BookmarksStack = createStackNavigator(
{
ParkBookmarks: {
screen: ParkBookmarksScreen,
params: {
bookmarks: this.state.bookmarks
}
},
ParkBookmarksDetails: {
screen: ParkDetailsScreen,
},
}
);
const AppNavigator = createBottomTabNavigator(
{
Bookmarks: BookmarksStack,
Finder: FinderStack,
}
);
const AppContainer = createAppContainer(AppNavigator);
return (
<AppContainer/>
);
}
}
export default App;
I'm using Apollo Client, but I've removed those parts.
In the Screen components, you can access the props like other ones using this.props.navigation.getParam('bookmarks').
One issue I encountered was that whenever I change the App state, I'm taken to the first screen. The state is updated, but it's a little disorienting. I'm not sure if there is a way to update the App state while staying on the screen. I can sort of understand that given the App state has updated, all the children need to be updated and so the current screen (which is part of a child I think) is reset. I don't know if that is a limitation of the system or a byproduct of how I designed the components.
I hope this helps someone. This seems to keep with the intended behavior of React Native. The library team really seems to want you to not use Redux. https://reactnavigation.org/docs/en/redux-integration.html

react-navigation doesnt work with react-native-lightbox

I am writing a project that navigates using react-navigation and i want to use lightbox however lightbox requires Navigator to function properly. I was hoping to see if i could write react-navigation support into the component and then allow a PR to happen. The possibility in my mind is using Navigator + react navigation so that i dont have to re-make my navigation because you could have both "this.props.navigator" and "this.props.navigation" but i am not sure how some of the navigation and navigator functions work.
I have been looking into lightbox.js at the occurences of Navigator and i am searching through the react-navigation api to see if i can find replacements for the functions used as it only occurs 8 times (2 of which are just checks to see if navigator is present).
lightbox.js - navigator occurences
open: function() {
this._root.measure((ox, oy, width, height, px, py) => {
this.props.onOpen();
this.setState({
isOpen: (this.props.navigator ? true : false),
isAnimating: true,
origin: {
width,
height,
x: px,
y: py,
},
}, () => {
if(this.props.navigator) {
create a route that will work with navigation (for react-navigation it would be the navigationOptions)
var route = {
component: LightboxOverlay,
passProps: this.getOverlayProps(),
};
fetch the current set of routes
var routes = this.props.navigator.getCurrentRoutes();
push a route onto the stack
routes.push(route);
some sort of reset for the stack that Navigator does (still looking into it, any help knowing what this does would be nice.
this.props.navigator.immediatelyResetRouteStack(routes);
Then the rest of the code for opening the lightbox runs
} else {
this.setState({
isOpen: true,
});
}
this.setTimeout(() => {
this.state.layoutOpacity.setValue(0);
});
});
});
},
for closing the stack the same lines are called but with .pop() instead of .push()
Anyone with experience of both react-navigation and react-native-lightbox would be a great help.