react native how to re render refresh screen on tabBarOnPress - reactjs

the screen do not get the new data until i close the app and open it again
i want to re-render the screen on tab press to refresh the screen and get the new data
Please see my below code
const DashboardTabNavigator = createBottomTabNavigator({
Home: { screen: Home,
navigationOptions: {
tabBarIcon: (
<Image style={{ width: 30, height: 30,tintColor:'white' }}
source={require('./assets/IconBootomTabNavigation/home_icon.png')} />
)
}
},
Category: { screen: Category,
navigationOptions: {
tabBarIcon: (
<Image style={{ width: 30, height: 30,tintColor:'white' }}
source={require('./assets/IconBootomTabNavigation/cat_icon.png')} />
)
}
}

I recommend you to place the code responsible for fetching the data inside componentDidMount lifecycle method. This way you can ensure you get the data after the initial render.
If the screens are not being re-rendered, please check this thread. You can use tabBarOnPress prop to call any component method.
You might also try this solution:
static navigationOptions = ({navigation}) => {
return {
tabBarOnPress: (tab, jumpToIndex) => {
if(!tab.focused) {
jumpToIndex(tab.index);
navigation.state.params.onFocus()
}
},
}
}
componentDidMount() {
this.props.navigation.setParams({
onFocus: your-data-fetching-method
})
}

Related

Implementing a button on the header bar, react native

I am trying to make a button on the right of the header bar so that it will change the order of the list, and i was thinking to have a state of the order and flip it as the button will be pressed and check wether that is true and false and show the list in the requested order.
But i get an error saying Notes.flipState is not a function. (In 'Notes.flipState()', 'Notes.flipState is undefined)
Any idea why is this hapening and how to solve it, below is my function flipState() and the navigationOptions.
state = {
desc: true
}
flipState() {
this.setState({ desc: !this.state.desc})
}
static navigationOptions = ({ flipState }) => {
return {
title: 'Upcoming Movies',
headerStyle: {
backgroundColor: 'blue',
},
headerTintColor: 'white',
headerTitleStyle: {
fontWeight: 'bold',
},
headerRight: () => (
<BorderlessButton
onPress={() => this.flipState()}
style={{ marginRight: 20 }}>
<Icon
name={'sort-alpha-asc'}
size={25}
color='gray'
/>
</BorderlessButton>
),
}
};
You can't call a "public" method inside of static method. "Play" with navigation state to pass data between header and content.
See setParams https://reactnavigation.org/docs/en/navigation-prop.html
A static method won't be able to access this .you have to pass function through the navigation params.
like
componentDidMount() {
this.props.navigation.setParams({
flipStat: this.flipStat,
});
}
get from navigationOption
static navigationOptions = ({ navigation }) => {
const flipStat = navigation.state.params.flipStat;
return </>
}

onpress event for icon in TabNavigator (react native with react-navigation)

I have an icon that i include in a tab navigator as per below:
const SearchTabBarIcon = ({ tintColor }) => (
<IconMCI
name="account-search"
size={45}
color={tintColor}
onPress={() => console.log('HELP!!')}
/>
);
....then my tabNavigator looks like this:
const SignedInTabNav = TabNavigator(
{
Profile: {
screen: Profile,
navigationOptions: {
tabBarLabel: 'Me',
tabBarIcon: ProfileTabBarIcon,
},
},
Search: {
screen: Search,
navigationOptions: {
tabBarLabel: 'Search',
tabBarIcon: SearchTabBarIcon,
},
},
tabBarComponent: TabBarBottom,
animationEnabled: false,
},
);
The console.log is fired when I click the icon, however the native functionality of the tabNavigator is lost.
How can I fire the onPress event but also maintain the navigation functionality?
Ive heard I should perhaps "add the props of navigation to my component in the render".
The render function of App.js looks like this:
render() {
const Layout = createRootNavigator(this.props.isAuthenticated);
return (
<View style={{ flex: 1, backgroundColor: '#F8F8FF' }}>
<Layout />
</View>
);
}
....and the function createRootNavigator looks like this
const createRootNavigator = (authenticated = false) =>
createSwitchNavigator(
{
SignedInTabNav: {
screen: SignedInTabNav,
},
SignedOutStackNav: {
screen: SignedOutStackNav,
},
},
{
initialRouteName: authenticated ? 'SignedInTabNav' : 'SignedOutStackNav',
},
);
....so where/how do I add the navigation prop in the render for App.js?...and will it be passed down through createRootNavigator.....and further through createSwitchNavigator....and finally passed into SignedInTabNav (TabNavigator) where the Search icon is referenced?
You can simply use tabBarOnPress callback to handle press events as an additional option, like this:
const SignedInTabNav = TabNavigator(
{
Profile: {
screen: Profile,
navigationOptions: {
tabBarLabel: 'Me',
tabBarIcon: ProfileTabBarIcon,
},
},
Search: {
screen: Search,
navigationOptions: {
tabBarLabel: 'Search',
tabBarIcon: SearchTabBarIcon,
tabBarOnPress: ({ navigation, defaultHandler }) => {
console.log('this will be fired just before nagivation happens')
defaultHandler() // if you omit this, navigation will not happen
}
},
},
tabBarComponent: TabBarBottom,
animationEnabled: false,
},
);
Solution was to use the 'didFocus' event from reactNavigation
https://reactnavigation.org/docs/en/function-after-focusing-screen.html
componentDidMount() {
const { navigation } = this.props;
this.focusListener = navigation.addListener('didFocus', () => {
// The screen is focused
// Call any action
});
}

Nested TabNavigator inside StackNavigator: controlling the header

I have a setup much like this:
let Tabs = createBottomTabNavigator({
screen1: Screen1,
screen2: Screen2
})
let Stack = createStackNavigator({
tabs: Tabs
otherScreen: OtherScreen
})
The stack navigator has a header, and that's fine. What I want is to get different header icons depending on what tab I'm currently on.
I'm using the following versions:
"react": "16.3.1",
"react-native": "~0.55.2",
"react-navigation": "^2.2.5"
I've considered switching up my setup so that each tab screen has its own StackNavigator but I like having the sliding animation when switching tabs and I don't want the header icons to slide along. The header bar should remain static but simply show different icons depending on the current tab.
You can use like this below, https://reactnavigation.org/docs/en/stack-navigator.html
//Screen1 Stack.
const Screen1 = createStackNavigator ({
Home: {
screen: Home,
navigationOptions: {
header: null //Need to set header as null.
}
}
});
//Screen2 Stack
const Screen2 = createStackNavigator ({
Profile: {
screen: Profile,
navigationOptions: {
header: null //Need to set header as null.
}
}
});
let Tabs = createMaterialTopTabNavigator({
Screen1:{
screen: Screen1 //Calling Screen1 Stack.
},
Screen2:{
screen: Screen2 //Calling Screen2 Stack.
}
},{ tabBarPosition: 'bottom' }) //this will set the TabBar at Bottom of your screen.
let Stack = createStackNavigator({
tabs:{
screen: Tabs, //You can add the NavigationOption here with navigation as parameter using destructuring.
navigationOptions: ({navigation})=>{
//title: (navigation.state.routes[navigation.state.index])["routeName"]
//this will fetch the routeName of Tabs in TabNavigation. If you set the routename of the TabNavigation as your Header.
//use the following title property,this will fetch the current stack's routeName which will be set as your header in the TabBar.
//title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
//you can use switch case,on matching the route name you can set title of the header that you want and also header left and right icons similarly.
switch ((navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName) {
case "Screen1":
return {
title: "Home",
headerLeft: (<Button
onPress={()=> alert("hi")}
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
case "Screen2":
return {
title: "Profile",
headerLeft: (<Button
onPress={()=> alert("hi")}
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
default:
return { title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName }
}
}
},
otherScreen:{
screen: OtherScreen
}
})
//navigationOptions
navigationOptions: ({navigation})=>{
//title: (navigation.state.routes[navigation.state.index])["routeName"]
//this will fetch the routeName of Tabs in TabNavigation. If you set the routename of the TabNavigation as your Header.
//use the following title property,this will fetch the current stack's routeName which will be set as your header in the TabBar.
//title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
switch ((navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName) {
case "Screen1":
return {
title: "Home",
headerLeft: (<Button
onPress={()=> alert("hi")} //Here you can able to set the back behaviour.
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
case "Screen2":
return {
title: "Profile",
headerLeft: (<Button
onPress={()=> alert("hi")}
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
default:
return { title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName }
}
}
//alert(navigation.state)
{
"routes":[
{
"key":"Screen1",
"routeName":"Screen1",
"routes":[
{
"key":"Home",
"routeName":"Home",
}
],
"index":0,
"isTransitioning":false,
"key":"id-1530276062643-0"
},
{
"key":"Screen2",
"routeName":"Screen2",
"routes":[
{
"key":"Profile",
"routeName":"Profile",
}
],
"index":0,
"isTransitioning":false,
"key":"id-1530276062643-0"
}
],
"index":0,
"isTransitioning":false,
"routeName":"tabs",
"key":"id-1530276062643-0"
}
//(navigation.state.routes[navigation.state.index])["routeName"]
//(navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
this will give the current route name of the tab inside StackNavigation.
The above code will set the title in root stack header where the TabBar resides as first route,so we are setting the header as null for the individual stack in TabBar. By using this way will provide Animation while switching the screens in TabBar since header will remain static.
you can find the working copy here https://www.dropbox.com/s/jca6ssn9zkzh9kn/Archive.zip?dl=0
Download this and Execute the following.
npm install //to get dependencies
react-native upgrade //to get android and ios folder
react-native link //to link dependencies and libraries
react-native run-ios (or) react-native run-android
you can use the above, Let me know if any.
You can achieve the desired behavior with the current navigation stack configuration of yours. You might need to change couple of things and combine couple of properties but it is fairly simple when you get yor head around it.
I try to explain it with a small example.
Consider having below navigators;
const Tabs = createBottomTabNavigator({
screen1: Tab1,
screen2: Tab2
})
const Stack = createStackNavigator({
tabs: {
screen: TabsPage,
navigationOptions: ({navigation}) => {
return { title: (navigation.state.params && navigation.state.params.title ? navigation.state.params.title : 'No Title' ) }
}
},
otherScreen: Page
})
As you can see I am setting the title parameter from the navigation state. To be ale to set the param for that navigator, we are going to get help from screenProps property;
class TabsPage extends Component {
onTabsChange = (title) => {
this.props.navigation.setParams({ title })
}
render() {
return(<Tabs screenProps={{ onTabsChange: this.onTabsChange }} />)
}
}
I created a wrapper component for tab navigator and passed down a function which is setting the title parameter.
For the last part we need to know how and when to use that function we passed down. For that we are going to use addListener navigation prop
class Tab1 extends React.Component {
setTitle = () => {
this.props.screenProps.onTabsChange('Title from Tab 1')
}
componentDidMount() {
this.props.navigation.addListener('willFocus', this.setTitle)
}
render() {
return <View><Text>{'Tab1'}</Text></View>
}
}
When our tab focused the passed function will run and then set the title for that tab. You can use this process for setting different buttons or icons for the header. You can find the working snack here.
const RootStack = createStackNavigator(
{
Home: {screen: HomeScreen},
FilterScreen: createMaterialTopTabNavigator({
Tab1: {screen:Tab1Screen},
Tab2: {screen: Tab2Screen},
}),
},
{
mode: 'modal',
headerMode: 'none',
}
);
render() {
return <RootStack/>;
}
In AppNavigation.js //or where you have defined your routes.
let Tabs = createBottomTabNavigator({
screen1: Screen1,
screen2: Screen2
})
let Stack = createStackNavigator({
tabs: Tabs
otherScreen: OtherScreen
},{
headerMode:"float", //Render a single header that stays at the top and animates as screens are changed. This is a common pattern on iOS.
headerTransitionPreset:"fade-in-place" //this will give a slight transition when header icon change.
}
)
In Screen1.js
class Screen1 extends Component {
static navigationOptions = ({ navigation }) => {
return {
...
headerLeft: <HeaderLeftImage navigation={navigation} image={"Image_For_Screen1"}/>,
...
}
}
...
}
NOTE: You can add title, headersStyle,headerRight in same way read this link header configuration for more detail
In Screen2.js do similar to Screen1
class Screen2 extends Component {
static navigationOptions = ({ navigation }) => {
return {
...
headerLeft: <HeaderLeftImage navigation={navigation} image={"Image_For_Screen2"}/>,
...
}
}
...
}
Header.js
export const HeaderLeftImage = (props) => (
<View style={{
'add styles'
}}>
<TouchableOpacity onPress={() => {"Add action here" }}>
<Image source={props.image} resizeMethod='resize' style={{ height: 30, width: 90, resizeMode: 'contain' }} />
</TouchableOpacity>
</View>
)
Hope this helps, if you have any doubt regarding the code feel free to ask.
If you are using react navigation <2 i.e ~1.5.* You can set it like this.
const Tabs = TabNavigator({
Tab1:{
screen: Tab1,
navigationOptions: ({navigation}) => {
return { title: "Tab 1 Heading", tabBarLabel:"Tab 1 "}
},
}
Tab2:{
screen: Tab2
navigationOptions: ({navigation}) => {
return { title: "Tab 2 Heading", tabBarLabel:"Tab 2 "}
}
}
})
const Stack = StackNavigator({
tabs: {
screen: Tabs
navigationOptions: ({navigation}) => {
return { title: "Stack"}
}
},
otherScreen: Page
})
I'm not sure why did they remove this feature, when you try the same it won't work on the latest react-navigation. Now title object key is used for fallback it seems.
This may be helpful some users. If you wish you can try downgrading also.
I have the Upgraded the React Navigation for my project I think this way will be useful to someone
const Tabs = TabNavigator({
Tab1:{
screen: Tab1,
navigationOptions: ({navigation}) => {
return { tabBarLabel:"Tab 1 "}
}},
Tab2:{
screen: Tab2
navigationOptions: ({navigation}) => {
return { tabBarLabel:"Tab 2 "}
}}
});
Tabs.navigationOptions = ({navigation})=>{
const { routeName } = navigation.state.routes[navigation.state.index]; //This gives current route
switch(routeName){
case "Tab1":
headerTitle="Tab 1";
break;
case "Tab1":
headerTitle="Tab 1";
break;
}
return {
headerTitle: headerTitle
}
}
You can simply use property headerShown: false. I have created a working example of react-navigation version: 5.x.x, nesting different navigators: stack, drawer & bottom-tabs.
Here's the link to github:
https://github.com/mvpbuddy/react-native-nested-navigators/
For a more detailed overview, you can checkout my blog:
https://mvpbuddy.io/blog/detail/how-to-build-an-app-with-nested-stack-drawer-bottom-tab-navigators

Override navigation bar title

I want to override the title on the navigation bar after the navigation has happened.
StackNavigator
const DashboardNavigator = new StackNavigator({
Dashboard: {
screen: HomeScreen,
navigationOptions: {
title: 'Home'
}
}
}, {
navigationOptions
});
I want to basically rename the title to something else when I got to the Home page. I know it's weird but need to handle dynamic titles.
This is what I have tried (HomeScreen separate component):
componentDidMount() {
this.props.navigation.setParams({
navigationOptions: {
title: 'New title'
}
});
}
This isn't making a difference. Any suggestions?
Also worth mentioning that I'm using redux.
You can try this:
StackNavigator
const DashboardNavigator = new StackNavigator({
Dashboard: {
screen: HomeScreen,
}
}, {
navigationOptions
});
HomeScreen In your homeScreen check if you have a dynamic title, if not set the title to "Home" but if exists set it to your dynamic one.
static navigationOptions = ({ navigation }) => ({
title: navigation.state.params ==='undefined' || navigation.state.params.title === 'undefined' ? 'Home': navigation.state.params.title
});
And then set your dynamic title like this:
componentDidMount() {
this.props.navigation.setParams({ title: 'your new title' })
}
in your home screen try this
Home Screen
static navigationOptions = ({ navigation }) => {
return {
title: navigation.state.params.headerTitle,
headerRight: (
<Icon
name="calendar"
type="octicon"
color="#fff"
/>
),
headerLeft: (
<Icon
name="keyboard-arrow-left"
color="#fff"
onPress={() => navigation.goBack(null)}
/>
),
headerStyle: {
backgroundColor: "#000",
paddingLeft: 10,
paddingRight: 10
},
headerTitleStyle: { color: "#fff", alignSelf: "center" }
};
};
componentDidMount() {
this.props.navigation.setParams({
headerTitle: 'New title'
});
}

Conditional routing using React Navigation (React Native)

Building a React Native app, and I would to send a user to the next screen DEPENDING on the initial Select screen's state. So, if there there is 1 item selected, it should take the user to the Single screen. If more than 1, the Multiple screen.
As you can see, I made custom headerLeft and headerRight components. I would prefer to keep the routing this way, but somehow add the initial state into this context. Is there a way?
If I could somehow decouple the navigation options from the routes.js file and put them into the Screen components themselves, that would be awesome. But from what I researched, it doesn't seem like that the navigationOptions can directly access state.
Here's the relevant code (routes.js):
export default StackNavigator(
{
Select: {
screen: Select,
navigationOptions: ({ navigation }) => ({
headerLeft: <View />,
headerRight: (
<HeaderRight
navigation={navigation}
destination=""
showIcon
/>
)
})
},
Single: {
screen: Single,
navigationOptions: ({ navigation }) => ({
headerLeft: (
<HeaderLeft navigation={navigation} destination="back" showIcon />
),
headerRight: (
<HeaderRight navigation={navigation} destination="SingleSecond" />
)
})
},
Multiple: {
screen: Multiple,
navigationOptions: ({ navigation }) => ({
headerLeft: <HeaderLeft navigation={navigation} destination="back" />,
headerRight: (
<HeaderRight navigation={navigation} destination="MultipleSecond" />
)
})
},
},
{
headerMode: "float",
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.routeName}`,
headerStyle: { backgroundColor: "#000" },
headerTitleStyle: {
color: "#fff",
textAlign: "center",
alignSelf: "center"
}
})
}
);
you can set params from a component these params can be accessed from navigation options.
this.props.navigation.setParams({ selectionState: "single" });
this.props.navigation.setParams({ selectionState: "multiple" });
and you can access the params from the navigation options like
static navigationOptions = ({navigation}) => {
let selected = navigation.state.params && navigation.state.params.selectionState;
return {
headerRight: ...
};
}
I hope this fills your needs, you need to set params after selecting something in your component. There is another way to dynamically set the initial state.
function getNavigation (selected = "single") {
return StackNavigator({
...,
{ initialState = selected === "single" ? "Single" : "Multiple" }
})
}
and then call this method with the selected argument to reset the screen.

Resources