Tabbed navigation react native react-navigation - react-native

I have an app working with tabbed navigation but it seems that I need to interact with the components in the tabs that aren't active when the app starts, before they'll display data.
I have 3 tabs in my app, a map that displays restaurants nearby, a list of different ingredients and also a list of additives.
All these data sets are being sourced from a server (salesforce) when the page is loaded that holds the tab nav -- the homescreen component. The only thing this component is doing is loading my three other components.
Now, when I click into the other tabs, the screen is blank until i scroll or click in the page somewhere and then the UI loads. I think this is due to the fact that the setState call has already run, but when the 1st component in the tab nav was visible to the user.
How can I fire a call to update the UI when someone clicks on the newly active tab? (i'm setting state still in the component, not using redux yet.. this will come with time!)..
component below:
import React, {Component} from 'react';
import {
View,
FlatList,
ActivityIndicator
} from 'react-native';
import {Icon, List, ListItem, SearchBar} from 'react-native-elements';
import {oauth, net} from 'react-native-force';
// todo - implement... import {StackNavigator} from 'react-navigation';
export default class Ingredients extends Component {
static navigationOptions = {
tabBarLabel: 'Ingredients',
title: 'Ingredients',
tabBarIcon: ({tintColor}) => (
<Icon
name='blur-linear'
color={tintColor}
/>
)
};
constructor(props) {
super(props);
this.state = {
ingredients: [],
refreshing: false
};
}
componentDidMount() {
oauth.getAuthCredentials(
() => this.fetchData(), // already logged in
() => {
oauth.authenticate(
() => this.fetchData(),
(error) => console.log('Failed to authenticate:' + error)
);
});
}
componentWillUnmount() {
this.setState({
ingredients: [],
refreshing: false
})
};
fetchData = () => {
this.setState({
refreshing: true
});
net.query('SELECT Id, Ingredient__c, CA_GF_Status_Code__c, CA_Ingredient_Notes__c FROM Ingredient__c ORDER BY Ingredient__c',
(response) => this.setState({
ingredients: response.records,
refreshing: false
})
);
};
renderHeader = () => {
//todo - make this actually search something
return <SearchBar placeholder="Type Here..." lightTheme round/>;
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large"/>
</View>
);
};
selectIcon = (statusCode) => {
switch (statusCode) {
case 0:
return <Icon type='font-awesome' name='close' color='#80A33F'/>;
case 1:
return <Icon type='font-awesome' name='check' color='#80A33F'/>;
case 2:
return <Icon type='font-awesome' name='asterisk' color='#80A33F'/>;
case 3:
return <Icon type='font-awesome' name='sign-out' color='#80A33F'/>;
case 4:
return <Icon type='font-awesome' name='question-circle' color='#80A33F'/>;
default:
return <Icon type='font-awesome' name='close' color='#80A33F'/>;
}
};
render() {
return (
<List>
<FlatList
data={this.state.ingredients}
renderItem={({item}) => (
<ListItem
title={item.Ingredient__c}
subtitle={item.CA_Ingredient_Notes__c}
chevronColor='#025077'
avatar={this.selectIcon(item.CA_GF_Status_Code__c)}
onPress={() => {window.alert('this is being pressed')}}
/>
)}
keyExtractor={(item, index) => index}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
refreshing={this.state.refreshing}
onRefresh={this.fetchData}
/>
</List>
);
}
}

Related

Unable to find an element with a testID

I'm building a React Native app. Within my GigsByDay component, there is a TouchableOpacity element which, when pressed, directs the user to a GigDetails screen. I'm trying to test this particular functionality using Jest and React Native Testing Library.
I've written the following test, but have received the error:
Unable to find an element with testID: gigs-today-card
The test is as follows:
describe("gigs by week component", () => {
let navigation;
beforeEach(() => {
navigation = { navigate: jest.fn() };
});
test("that when gig listing is pressed on it redirects user to Gig Details page", () => {
render(<GigsByDay navigation={navigation} />);
const gigCard = screen.getByTestId("gigs-today-card");
fireEvent.press(gigCard);
expect(navigation.navigate).toHaveBeenCalledWith("GigDetails");
});
});
The element it's testing is as follows:
<TouchableOpacity
testID="gigs-today-card"
style={styles.gigCard}
onPress={() =>
navigation.navigate('GigDetails', {
venue: item.venue,
gigName: item.gigName,
blurb: item.blurb,
isFree: item.isFree,
image: item.image,
genre: item.genre,
dateAndTime: {...item.dateAndTime},
tickets: item.tickets,
id:item.id
})
}>
<View style={styles.gigCard_items}>
<Image
style={styles.gigCard_items_img}
source={require('../assets/Icon_Gold_48x48.png')}
/>
<View>
<Text style={styles.gigCard_header}>{item.gigName}</Text>
<Text style={styles.gigCard_details}>{item.venue}</Text>
</View>
</View>
</TouchableOpacity>
I've tried fixing my test as follows, but to no success:
test("that when gig listing is pressed on it redirects user to Gig Details page", async () => {
render(<GigsByDay navigation={navigation} />);
await waitFor(() => {
expect(screen.getByTestId('gigs-today-card')).toBeTruthy()
})
const gigCard = screen.getByTestId("gigs-today-card");
fireEvent.press(gigCard);
expect(navigation.navigate).toHaveBeenCalledWith("GigDetails");
});
});
Any suggestions on how to fix this? I also tried assigning the testID to the view within the TouchableOpacity element.
For context, here's the whole GigsByDay component:
import { FC } from 'react';
import { FlatList,TouchableOpacity,StyleSheet,View,Image,Text } from 'react-native'
import { listProps } from '../routes/homeStack';
import { GigObject } from '../routes/homeStack';
type ListScreenNavigationProp = listProps['navigation']
interface Props {
gigsFromSelectedDate: GigObject[],
navigation: ListScreenNavigationProp
}
const GigsByDay:FC<Props> = ({ gigsFromSelectedDate, navigation }):JSX.Element => (
<FlatList
testID='gigs-today'
data={gigsFromSelectedDate}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<TouchableOpacity
testID="gigs-today-card"
style={styles.gigCard}
onPress={() =>
navigation.navigate('GigDetails', {
venue: item.venue,
gigName: item.gigName,
blurb: item.blurb,
isFree: item.isFree,
image: item.image,
genre: item.genre,
dateAndTime: {...item.dateAndTime},
tickets: item.tickets,
id:item.id
})
}>
<View style={styles.gigCard_items}>
<Image
style={styles.gigCard_items_img}
source={require('../assets/Icon_Gold_48x48.png')}
/>
<View>
<Text style={styles.gigCard_header}>{item.gigName}</Text>
<Text style={styles.gigCard_details}>{item.venue}</Text>
</View>
</View>
</TouchableOpacity>
)}
/>
)
Please pass the gigsFromSelectedDate prop with some mock array data so that the flat list would render its elements based on the array length. Currently, you are not passing it. Please check the code below.
test('that when gig listing is pressed on it redirects user to Gig Details page', () => {
const mockData = [{venue: 'some venue',
gigName: 'some gigName,
blurb: 'some blurb',
isFree: 'some isFree',
image: 'some image',
genre: 'some genre',
dateAndTime: {},
tickets: ['some ticket'],
id: 'some id'}]
const screen = render(<GigsByDay navigation={navigation} gigsFromSelectedDate={mockData} />);
const gigCard = screen.getByTestId('gigs-today-card');
fireEvent.press(gigCard);
expect(navigation.navigate).toHaveBeenCalledWith('GigDetails');
});
Have you installed below package?
please check your Package.json
"#testing-library/jest-native": "^5.4.1"
if not please install it
then import it where you have written test cases.
import '#testing-library/jest-native/extend-expect';
if it is already present in then try below properties of jest-native.
const gigCard = screen.queryByTestId("gigs-today-card");
const gigCard = screen.findByTestId("gigs-today-card");

React Navigation 5 headerRight button function called doesn't get updated states

In the following simplified example, a user updates the label state using the TextInput and then clicks the 'Save' button in the header. In the submit function, when the label state is requested it returns the original value '' rather than the updated value.
What changes need to be made to the navigation headerRight button to fix this issue?
Note: When the Save button is in the render view, everything works as expected, just not when it's in the header.
import React, {useState, useLayoutEffect} from 'react';
import { TouchableWithoutFeedback, View, Text, TextInput } from 'react-native';
export default function EditScreen({navigation}){
const [label, setLabel] = useState('');
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableWithoutFeedback onPress={submit}>
<Text>Save</Text>
</TouchableWithoutFeedback>
),
});
}, [navigation]);
const submit = () => {
//label doesn't return the updated state here
const data = {label: label}
fetch(....)
}
return(
<View>
<TextInput onChangeText={(text) => setLabel(text) } value={label} />
</View>
)
}
Label should be passed as a dependency for the useLayouteffect, Which will make the hook run on changes
React.useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableWithoutFeedback onPress={submit}>
<Text>Save</Text>
</TouchableWithoutFeedback>
),
});
}, [navigation,label]);
Guruparan's answer is correct for the question, although I wanted to make the solution more usable for screens with many TextInputs.
To achieve that, I added an additional state called saving, which is set to true when Done is clicked. This triggers the useEffect hook to be called and therefore the submit.
export default function EditScreen({navigation}){
const [label, setLabel] = useState('');
const [saving, setSaving] = useState(false);
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableWithoutFeedback onPress={() => setSaving(true)}>
<Text>Done</Text>
</TouchableWithoutFeedback>
),
});
}, [navigation]);
useEffect(() => {
// Check if saving to avoid calling submit on screen unmounting
if(saving){
submit()
}
}, [saving]);
const submit = () => {
const data = {label: label}
fetch(....)
}
return(
<View>
<TextInput onChangeText={(text) => setLabel(text) } value={label} />
</View>
)
}

How do i pass value from screen to bottomtab icon badge?

I want to be able to have badges for one of the icons for my bottom tabs (Reminders) but i do not know how to pass the value such that the badge will show the number from the value given from the reminderscreen.
So how would I go about this? Im quite confused with how react navigation works. Any help would be much appreciated! Thanks!
In App.js
const TabStack = createBottomTabNavigator(
{
Home : { screen: HomeStack },
Reminders: { screen: ReminderStack,
navigationOptions: ({ screenProps }) => ({
tabBarIcon: ({tintColor}) =>
<View style={{flexDirection: 'row',alignItems: 'center',justifyContent: 'center',}}>
<IconBadge
MainElement={
<View style={{
marginRight:15
}}>
<Ionicons name={`ios-alarm`} size={30} color={tintColor} />
</View>
}
BadgeElement={
<Text style={{color:'#FFFFFF'}}>{screenProps.notifCount}</Text>
}
Hidden={true}
IconBadgeStyle={
{width:20,
height:20,
backgroundColor: 'red'}
}
/>
</View>
})
},
},
{
defaultNavigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, horizontal, tintColor }) => {
const { routeName } = navigation.state;
let IconComponent = Ionicons;
let iconName;
if (routeName === 'Home') {
iconName = `ios-home`;
} else if (routeName === 'Reminders') {
iconName = `ios-alarm`;
}
return <IconComponent name={iconName} size={25} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: '#0892d0',
inactiveTintColor: 'gray',
},
}
);
In ReminderScreen.js
import React from 'react';
import { Text, View, TouchableOpacity, StyleSheet,Image,ScrollView,Dimensions} from 'react-native';
import { ListItem, withBadge,Badge, } from 'react-native-elements';
import { Container, Header, Tab, Tabs, ScrollableTab } from 'native-base';
import R_Equipment from './rEquipmentTab';
import R_Room from './rRoomTab';
import axios from 'axios';
export default class ReminderScreen extends React.Component {
render() {
return (
//I want to pass a notifCount variable back to Tabstack
//e.g. notifCount = 5
<Container>
<Tabs renderTabBar={()=> <ScrollableTab />}>
<Tab heading="Rooms">
<R_Room navigation={this.props.navigation} />
</Tab>
<Tab heading="Equipment">
<R_Equipment navigation={this.props.navigation} />
</Tab>
</Tabs>
</Container>
);
}
}
const styles = StyleSheet.create({
displayImage: {
height: 50,
width: 100,
borderRadius: 10,
},
});
You can send it as parent to child, or you can use state, if it's easier you can use Redux
//This use vanilla.js or state common
<YourComponent = badgeData={this.state.badgedata} />
// In your second class/child, get the data as props
this.props.badgeData
Or for more detail, you cant follow the link below :
Here

What is the error when executing a FLATLIST?

When executing sends me the following error, do you know what I'm doing wrong?
I used the code that is published
Invariant Violation: Element type is invalid: expected a string (for
built-in components) or a class/function (for composite components)
but got: undefined. You likely forgot to export your component from
the file it's defined in, or you might have mixed up default and named
imports.
Check the render method of FlatListDemo.
This error is located at: in FlatListDemo (at withExpoRoot.js:22) in
RootErrorBoundary (at withExpoRoot.js:21) in ExpoRootComponent (at
renderApplication.js:34) in RCTView (at View.js:44) in RCTView (at
View.js:44) in AppContainer (at renderApplication.js:33)
node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:5630:10
in createFiberFromElement
node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:9710:8
in reconcileSingleElement ... 21 more stack frames from framework
internals
import React, { Component } from "react";
import { View, Text, FlatList, ActivityIndicator } from "react-native";
import { List, ListItem, SearchBar } from "react-native-elements";
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const { page, seed } = this.state;
const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: page === 1 ? res.results : [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({ error, loading: false });
});
};
handleRefresh = () => {
this.setState(
{
page: 1,
seed: this.state.seed + 1,
refreshing: true
},
() => {
this.makeRemoteRequest();
}
);
};
handleLoadMore = () => {
this.setState(
{
page: this.state.page + 1
},
() => {
this.makeRemoteRequest();
}
);
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
renderHeader = () => {
return <SearchBar placeholder="Type Here..." lightTheme round />;
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
avatar={{ uri: item.picture.thumbnail }}
containerStyle={{ borderBottomWidth: 0 }}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
onRefresh={this.handleRefresh}
refreshing={this.state.refreshing}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={50}
/>
</List>
);
}
}
export default FlatListDemo;
It looks like you were following this tutorial on medium https://medium.freecodecamp.org/how-to-build-a-react-native-flatlist-with-realtime-searching-ability-81ad100f6699
Unfortunately this tutorial was written at a time before the react-native-elements were upgraded to v1.0.0. When react-native-elements was upgraded several components were dropped, and others were changed. For a full list of them you should see this blog post on their website. It is too long to replicate here but I will repeat the parts relevant to your specific situation.
List
This have been removed and is what is probably causing the big error that you are seeing as you are trying to import something that doesn't exist anymore.
https://react-native-training.github.io/react-native-elements/blog/2019/01/27/1.0-release.html#list
List component has been removed! List was just a regular React Native
View with some small margin styles. It wasn't actually needed to use
the ListItem component. Instead we recommend using the FlatList or
SectionList components from React Native which function both as Views
and also displaying items, pull to refresh and more.
ListItem
roundAvatar and avatarhave been dropped, and are no longer in use.
https://react-native-training.github.io/react-native-elements/blog/2019/01/27/1.0-release.html#listitem
avatar, avatarStyle, avatarContainerStyle, roundAvatar, and
avatarOverlayContainerStyle removed. Avatars can now be customized
using the rightAvatar and leftAvatar props which can either render a
custom element or an object that describes the props from Avatar.
Solution
You have two choices.
Downgrade to v0.19.1
Refactor your code for v1.0.0
Downgrade
The simplest (though this may not work as there may be compatibility issues with newer versions of react-native) is to downgrade your version of react-native-elements.
You can do that by running npm uninstall react-native-elements
and then reinstall the specific version npm install react-native-elements#0.19.1
You can see a full list of the v0.19.1 components here https://react-native-training.github.io/react-native-elements/docs/0.19.1/overview.html
Refactor
The other choice, and probably the better choice though arguably it will require more work, is to refactor your code so that it uses the new components from v1.0.0.
You can see a full list of the v1.0.0 components here https://react-native-training.github.io/react-native-elements/docs/overview.html
As Andres says, there are properties of react-native elements that changed therefore I will publish the code that in my case worked.
import React, { Component } from "react";
import { View, Platform, Image, Text, FlatList, ActivityIndicator } from "react-native";
import { ListItem, List } from "react-native-elements";
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: []
}
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = 'https://randomuser.me/api/?seed=1&page=1&results=20';
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
loading: false,
});
});
};
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
title={
<View >
<Image style={{height:50}} source={require('./src/img/recarga.jpg')}>
</Image>
<Text>{item.name}</Text>
</View>
}
subtitle={item.email}
/>
)}
keyExtractor={item=>item.email}
/>
</View>
);
}
}
export default FlatListDemo;

How To Reload View Tap on TabNavigator in React Native

I want to reload the tabNavigator when the user changse the tab each time. the lifecycle method of react native doesn't get called when user changes the tab. Then how can it be possible to reload tab in TabNavigator :
The below example have two Tabs : 1) Home 2)Events. Now I want to refresh the event Tab when user returns from the home tab.
EXPO LINK : Expo Tab Navigator
Code :
import React, {Component} from 'react';
import {View, StyleSheet, Image, FlatList, ScrollView, Dimensions } from 'react-native';
import { Button, List, ListItem, Card } from 'react-native-elements' // 0.15.0
//import { Constants } from 'expo';
import { TabNavigator, StackNavigator } from 'react-navigation'; // 1.0.0-beta.11
//image screen width and height defs
const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;
export default class App extends Component {
render() {
//const { navigate } = this.props.navigation;
return (
<TabsNav />
)
}
}
class MyHomeScreen extends Component {
render() {
return (
<View>
<Image
resizeMode="cover"
style={{ width: windowWidth * .85, height: windowHeight * 0.3}}
source={{uri: 'http://www.ajaxlibrary.ca/sites/default/files/media/logo.png?s358127d1501607090'}}
/>
<Button
onPress={() => this.props.navigation.navigate('Notifications')}
title="Go to notifications"
/>
</View>
);
}
}
class AplEvents extends Component {
static navigationOptions = {
tabBarLabel: 'Events List',
tabBarIcon: ({ tintColor }) => (
<Image
source={{uri: 'https://facebook.github.io/react/img/logo_og.png'}}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
};
constructor(props) {
super(props);
this.state = {
data: [],
error: null,
};
}
// when component mounts run the function fetch
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `http://www.ajaxlibrary.ca/?q=calendar-test`;
fetch(url)
.then((res) => res.json())
.then((res) => {
this.setState({
data: [...this.state.data, ...res.nodes],
error: res.error || null,
});
})
.catch(error => {
this.setState( error );
});
};
render() {
const { navigate } = this.props.navigation;
return (
<List containerStyle={{ marginTop: 0, borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
//squareAvatar
title={`${item.node.title}\n${item.node.Program_Location}`}
subtitle={item.node.Next_Session}
avatar={{ uri: item.node.Image }}
containerStyle={{ borderBottomWidth: 0 }}
// save params to pass to detailed events screen
onPress={() => navigate('Details', {title: `${item.node.title}`,
body: `${item.node.Body}`,
date: `${item.node.Date}`,
Next_Session: `${item.node.Next_Session}`,
Program_Location: `${item.node.Program_Location}`,
Nid: `${item.node.Nid}`,
Image: `${item.node.Image}`,
Run_Time: `${item.node.Run_Time}`})}
/>
)}
keyExtractor={item => item.node.Nid}
/>
</List>
);
}
}
class EventDetails extends Component {
static navigationOptions = {
title: 'EventDetails'
};
render() {
const { params } = this.props.navigation.state;
let pic = {
uri: `${params.Image}`
};
//const pic = { params.Image };
return (
<ScrollView>
<Card
title={params.title}
>
<Image
resizeMode="cover"
style={{ width: windowWidth * .85, height: windowHeight * 0.3}}
source={pic}
/>
<Button style={{marginTop: 10}}
icon={{name: 'date-range'}}
backgroundColor='#03A9F4'
fontFamily='Lato'
buttonStyle={{borderRadius: 0, marginLeft: 0, marginRight: 0, marginBottom: 0}}
title='Add to Calendar'
/>
<ListItem
title="Event Description"
subtitle={params.body}
hideChevron='true'
/>
<ListItem
title="Date"
subtitle={`${params.Next_Session}\n Run Time - ${params.Run_Time}`}
hideChevron='true'
/>
<ListItem
title="Location"
subtitle={params.Program_Location}
hideChevron='true'
/>
</Card>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
icon: {
width: 26,
height: 26,
},
});
const EventStack = StackNavigator({
EventList: {
screen: AplEvents,
navigationOptions: {
title: "APL Event Listing",
}
},
Details: {
screen: EventDetails,
},
TabsNav: { screen: MyHomeScreen}
});
const TabsNav = TabNavigator({
Home: {
screen: MyHomeScreen,
navigationOptions: {
tabBarLabel: 'Home',
tabBarIcon: ({ tintColor }) => (
<Image
source={{uri: 'https://upload.wikimedia.org/wikipedia/de/thumb/9/9f/Twitter_bird_logo_2012.svg/154px-Twitter_bird_logo_2012.svg.png'}}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
},
},
EventList: {
screen: EventStack,
navigationOptions: {
tabBarLabel: 'Events',
tabBarIcon: ({ tintColor }) => (
<Image
source={{uri: 'https://upload.wikimedia.org/wikipedia/de/thumb/9/9f/Twitter_bird_logo_2012.svg/154px-Twitter_bird_logo_2012.svg.png'}}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
},
},
}, {
tabBarOptions: {
activeTintColor: '#e91e63',
},
});
React Native Tab Navigation has an option prop as unmountOnBlur set it to true and it will reload the tab screens every time you click on tabs.
<Tab.Screen name={"Profile"} component={ProfileScreen} options={{unmountOnBlur: true}} />
Doc/Ref - RN Bottom tab Navigator docs
.
Update - There is a hook called as useIsFocused in '#react-navigation/native'.
Use this with useEffect to re-render the screen every time it is focused or used .
import { useIsFocused } from '#react-navigation/native';
const isFocused = useIsFocused();
useEffect(() => { yourApiCall(); }, [isFocused])
look at this link. My problem is solving thanks to this.
<Tabs.Navigator
initialRouteName="Home"
tabBar={(props) => (
<TabBar {...props} />
)}>
<Tabs.Screen
name="Home"
component={HomeView}
options={{ unmountOnBlur: true }}
listeners={({ navigation }) => ({
blur: () => navigation.setParams({ screen: undefined }),
})}
/>
</Tabs.Navigator>
https://github.com/react-navigation/react-navigation/issues/6915#issuecomment-692761324
There's many long discussion about this from react-native issue 314, 486, 1335, and finally we got a way to handle this, after Sep 27, 2017, react-navigation version v1.0.0-beta.13:
New Features
Accept a tabBarOnPress param (#1335) - #cooperka
So here we go,
Usage:
const MyTabs = TabNavigator({
...
}, {
tabBarComponent: TabBarBottom /* or TabBarTop */,
tabBarPosition: 'bottom' /* or 'top' */,
navigationOptions: ({ navigation }) => ({
tabBarOnPress: (scene, jumpToIndex) => {
console.log('onPress:', scene.route);
jumpToIndex(scene.index);
},
}),
});
I wasn't able to get this to work, and after checking the React Navigation documentation, found this, which seems to suggest that later versions (I'm using 1.0.0-beta.27) changed the method signature to a single object:
tabBarOnPress Callback to handle tap events; the argument is an object
containing:
the previousScene: { route, index } which is the scene we are leaving
the scene: { route, index } that was tapped
the jumpToIndex method
that can perform the navigation for you
https://reactnavigation.org/docs/en/tab-navigator.html#tabbaronpress
Given that, and the code from beausmith here, I put this together.
navigationOptions: ({ navigation }) => ({
tabBarOnPress: (args) => {
if (args.scene.focused) { // if tab currently focused tab
if (args.scene.route.index !== 0) { // if not on first screen of the StackNavigator in focused tab.
navigation.dispatch(NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: args.scene.route.routes[0].routeName }) // go to first screen of the StackNavigator
]
}))
}
} else {
args.jumpToIndex(args.scene.index) // go to another tab (the default behavior)
}
}
})
Note that you'll need to import NavigationActions from react-navigation for this to work.
Hope this helps somebody :)