I am trying to use this with react-navigation and I keep getting:
fontFamily 'Feather' is not a system font and has not been loaded
through Expo.Font.loadAsync
I am using the Font.loadAsync() method in my loading component but the error keeps showing up. I actually ended up removing all vector icons from my app in order to start clean.
So my question is if I have a switch navigator which starts with a loading screen (this starts the auth check) and switches either to login screen or root tab container which contains the meat of my app. I cant seem to figure out where to call Font.loadAsync()
Here is my MainNavigator.js
export default createBottomTabNavigator(
{
Friends: FriendsList,
Profile: Profile,
Settings: Settings
},
{
defaultNavigationOptions: ({navigation}) => ({
tabBarIcon: ({tintColor}) => {
const {routeName} = navigation.state;
let iconName;
if (routeName == "Friends") {
iconName = "users"
} else if (routeName == "Profile") {
iconName = "user"
} else if (routeName == "Settings") {
iconName = "settings"
}
return <Feather name={iconName} size={20} color={tintColor} />
}
}),
tabBarOptions: {
style: {
backgroundColor: "#fff"
},
inactiveTintColor: "#999",
activeTintColor: TERTIARY_COLOR
}
}
)
App.js
// Screens for Switch Navigator
import AuthScreens from "./components/screens/AuthScreens";
import LoadingScreen from "./components/screens/Loading";
import MainNavigator from "./MainNavigator";
const AppContainer = createAppContainer(createSwitchNavigator(
{
LoadingScreen,
AuthScreens,
MainNavigator
},
{
initialRouteName: "LoadingScreen"
}
))
Loading Screen
export default class LoadingScreen extends Component {
async componentDidMount() {
try{
await Font.loadAsync({
FeatherFont
})
firebase.auth().onAuthStateChanged(user => {
this.props.navigation.navigate(user ? "MainContainer" : "LoginScreen")
})
} catch(error) {
alert(error)
}
}
render() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center"}}>
<ActivityIndicator/>
</View>
)
}
}
You need to load them in the App.js inside _loadResourcesAsync unsing the Font.loadAsync()
_loadResourcesAsync = async () => {
// blablabla
return Promise.all([
Asset.loadAsync([
require('random images'),
]),
Font.loadAsync(
// this line is where you load the font, name and path
{Feather: require('./assets/fonts/Feather-path.ttf')},
),
]);
};
And then inside the render function :
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<AppNavigator style={styles.container} />
<BarraOffline connected={this.state.isConnected}/>
</View>
);
}
}
I don't know your definition of FeatherFont,
but I don't think Font recognize it as a font.
Font is the module that deals with font-related tasks.
First, we must load the font from our assets directory using
Expo.Font.loadAsync().
We can do this in the componentDidMount() lifecycle method of the
App component.
Add the following method in App:
Now that we have the font files saved to disk and the Font SDK
imported, let's add this code:
We need a way to re-render the Text component when the font has finished loading.
First we initialize fontLoaded to false in the App class
constructor:
class App extends React.Component {
state = {
fontLoaded: false,
};
async componentDidMount() {
await Font.loadAsync({
'open-sans-bold': require('./assets/fonts/OpenSans-Bold.ttf'),
});
this.setState({ fontLoaded: true });
}
}
With React Native you specify fonts in Text components using the fontFamily style property. The fontFamily is the key that we used with Font.loadAsync.
Usage
<Text style={{ fontFamily: 'open-sans-bold', fontSize: 56 }}>
Hello, world!
</Text>
Related
Situation:
The react native app has a BottomTabNavigator (react-navigation/material-bottom-tabs) and one of the tabs has a NativeStackNavigator (react-navigation/native-stack).
BottomTabNavigator:
tab1
tab2
tab3:
NativeStackNavigator:
screen1
screen2
Problem:
When I press tab3 (from the BottomTabNavigator) I see screen 1.
If I press a button on screen 1 then it will navigate from screen 1 to screen 2.
When I press tab1 or tab2 and then I press tab3 again then I see screen 1.
I want to see screen 2 (because that's what should be at the top of the stack from the NativeStackNavigator right?).
Did the stack from NativeStackNavigator reset?
Did the whole NativeStackNavigator render again?
What causes this behavior?
Code BottomTabNavigator:
export type MainNavigationBarParam = {
tab1: undefined;
tab2: undefined;
tab3: undefined;
};
const Tab = getMainNavigationBarTabNavigator();
export const tab3Stack = createNativeStackNavigator();
export function MainNavigationBar() {
const sizeToUse = 25;
return (
<Tab.Navigator
screenOptions={defaultScreenOptions()}
barStyle={{backgroundColor: theme.colors.primary}}>
<Tab.Screen
name="tab1"
component={Component1}
/>
<Tab.Screen
name="tab2"
component={Component2}
/>
<Tab.Screen
name="tab3"
component={Component3}
/>
</Tab.Navigator>
);
}
function defaultScreenOptions() {
const screenOptions: any = {
headerShown: false,
tabBarHideOnKeyboard: true,
};
if (Platform.OS === 'web') {
screenOptions.swipeEnabled = false;
}
return screenOptions;
}
Code Component3:
export type Tab3Stack ParamList = {
Screen1: undefined;
Screen2: {id: string; name: string};
};
export default function Component3() {
const [topComponentHeight, setTopComponentHeight] = useState(0);
function onLayout(event: LayoutChangeEvent) {
if ('top' in event.nativeEvent.layout) {
const withTop = event.nativeEvent.layout as unknown as {top: number};
setTopComponentHeight(withTop.top);
}
event;
}
return (
<View style={navContainerStyle(topComponentHeight).navContainer} onLayout={onLayout}>
<tab3Stack.Navigator screenOptions={{headerShown: false}}>
<tab3Stack.Screen
name={'Screen1'}
component={Screen1}
/>
<tab3Stack.Screen
name={'Screen2'}
component={Screen2}
/>
</tab3Stack.Navigator>
</View>
);
}
Update 1
Reproduced the code above for 2 tabs: https://snack.expo.dev/#jacobdel/182747
The problem does not occur on snack, only in my app.
Console in chrome:
No errors or warnings.
Sometimes this is shown, but after disabling the quill editor it doesn't appear anymore
Update 2
Cause is found: getMainNavigationBarTabNavigator(); from BottomTabNavigator
File MainNavigationBarTabNavigator.ts looks like this:
import {createMaterialBottomTabNavigator} from '#react-navigation/material-bottom-tabs';
import {MainNavigationBarParam} from './MainNavigationTabs';
export function getMainNavigationBarTabNavigator() {
return createMaterialBottomTabNavigator<MainNavigationBarParam>();
}
While MainNavigationBarTabNavigator.web.ts looks like this:
import {createMaterialTopTabNavigator} from '#react-navigation/material-top-tabs';
import {MainNavigationBarParam} from './MainNavigationTabs';
export function getMainNavigationBarTabNavigator() {
return createMaterialTopTabNavigator<MainNavigationBarParam>();
}
Snack only works as intended when using MainNavigationBarTabNavigator.ts
Update 3
Stuck..
Replacing the code from MainNavigationBarTabNavigator.web.ts with the code from MainNavigationBarTabNavigator.ts does not show the intended behavior as shown in the Snack example.
There seems to be an issue with the navigation event handling of #react-navigation/material-top-tabs.
If we handle this on our own, then the nested stack is not reset. We can prevent the default navigation action by using a tabPress event listener and calling e.preventDefault.
In your case, this is done as follows for screen2 (which is the nested stack).
<Tab.Screen
name="screen2"
component={Screen2}
listeners={({ navigation, route }) => ({
tabPress: (e) => {
e.preventDefault();
navigation.navigate({ name: route.name, merge: true });
},
})}
/>
Notice that this works fine on the phone, but has some issues on the web. If we navigate fast between screen2 and screen1 multiple times, then the navigation does not work at all. I haven't found the root cause for this issue.
However, we can make this work on the web as well as on the phone by providing a custom top navigation bar using the tabBar prop of the tab navigator. We override the default behavior of the tabPress function.
The original component is exported and is named MaterialTopTabBar. Sadly, it does not provide the possibility to provide a custom onTabPress function via props.
I have forked the component and 'fixed' (without knowing what exactly is going wrong here) the onTabPress function.
import {
useTheme,
} from '#react-navigation/native';
import Color from 'color';
import * as React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { TabBar, TabBarIndicator } from 'react-native-tab-view';
export default function FixedTabBarTop({
state,
navigation,
descriptors,
...rest
}) {
const { colors } = useTheme();
const focusedOptions = descriptors[state.routes[state.index].key].options;
const activeColor = focusedOptions.tabBarActiveTintColor ?? colors.text;
const inactiveColor =
focusedOptions.tabBarInactiveTintColor ??
new Color(activeColor).alpha(0.5).rgb().string();
return (
<TabBar
{...rest}
navigationState={state}
scrollEnabled={focusedOptions.tabBarScrollEnabled}
bounces={focusedOptions.tabBarBounces}
activeColor={activeColor}
inactiveColor={inactiveColor}
pressColor={focusedOptions.tabBarPressColor}
pressOpacity={focusedOptions.tabBarPressOpacity}
tabStyle={focusedOptions.tabBarItemStyle}
indicatorStyle={[
{ backgroundColor: colors.primary },
focusedOptions.tabBarIndicatorStyle,
]}
indicatorContainerStyle={focusedOptions.tabBarIndicatorContainerStyle}
contentContainerStyle={focusedOptions.tabBarContentContainerStyle}
style={[{ backgroundColor: colors.card }, focusedOptions.tabBarStyle]}
getAccessibilityLabel={({ route }) =>
descriptors[route.key].options.tabBarAccessibilityLabel
}
getTestID={({ route }) => descriptors[route.key].options.tabBarTestID}
onTabPress={({ route }) => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!event.defaultPrevented) {
navigation.navigate({ name: route.name, merge: true });
}
}}
onTabLongPress={({ route }) =>
navigation.emit({
type: 'tabLongPress',
target: route.key,
})
}
renderIcon={({ route, focused, color }) => {
const { options } = descriptors[route.key];
if (options.tabBarShowIcon === false) {
return null;
}
if (options.tabBarIcon !== undefined) {
const icon = options.tabBarIcon({ focused, color });
return (
<View style={[styles.icon, options.tabBarIconStyle]}>{icon}</View>
);
}
return null;
}}
renderLabel={({ route, focused, color }) => {
const { options } = descriptors[route.key];
if (options.tabBarShowLabel === false) {
return null;
}
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
if (typeof label === 'string') {
return (
<Text
style={[styles.label, { color }, options.tabBarLabelStyle]}
allowFontScaling={options.tabBarAllowFontScaling}
>
{label}
</Text>
);
}
return label({ focused, color });
}}
renderBadge={({ route }) => {
const { tabBarBadge } = descriptors[route.key].options;
return tabBarBadge?.() ?? null;
}}
renderIndicator={({ navigationState: state, ...rest }) => {
return focusedOptions.tabBarIndicator ? (
focusedOptions.tabBarIndicator({
state: state,
...rest,
})
) : (
<TabBarIndicator navigationState={state} {...rest} />
);
}}
/>
);
}
const styles = StyleSheet.create({
icon: {
height: 24,
width: 24,
},
label: {
textAlign: 'center',
textTransform: 'uppercase',
fontSize: 13,
margin: 4,
backgroundColor: 'transparent',
},
});
I have used it as follows.
export function MainNavigationBar() {
return (
<Tab.Navigator
tabBar={props => <FixedTabBarTop {...props} />}
screenOptions={defaultScreenOptions()}>
<Tab.Screen
name="screen1"
component={Screen1}
/>
<Tab.Screen
name="screen2"
component={Screen2}
/>
</Tab.Navigator>
);
}
I have updated your snack. You might want to submit an issue on GitHub as well. It feels like a bug.
The picture file I drew is in ASSETS
You can tell from this picture
I want to set the time when this image appears
I don't know much about coding because I'm a beginner
I want you to modify my whole code
I really want to complete this app
Please help me
I want to make the splash image appear for about 5 seconds
import React, { useRef, useState, useCallback, useEffect } from "react";
import { BackHandler, Platform, StyleSheet,ActivityIndicator } from "react-native";
import { WebView } from "react-native-webview";
export default function App() {
const webView = useRef();
const [canGoBack, setCanGoBack] = useState(false);
const handleBack = useCallback(() => {
if (canGoBack && webView.current) {
webView.current.goBack();
return true;
}
return false;
}, [canGoBack]);
useEffect(() => {
BackHandler.addEventListener("hardwareBackPress", handleBack);
return () => {
BackHandler.removeEventListener("hardwareBackPress", handleBack);
};
}, [handleBack]);
const App = () => (
<View style={[styles.container, styles.horizontal]}>
<ActivityIndicator />
<ActivityIndicator size="large" />
<ActivityIndicator size="small" color="#0000ff" />
<ActivityIndicator size="large" color="#00ff00" />
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
horizontal: {
flexDirection: 'row',
justifyContent: 'space-around',
padding: 10,
},
});
const platformStyles = StyleSheet.create({
webView: Platform.OS === 'ios'
? { marginTop: 30, marginBottom: 40 }
: { marginTop: 30 }
});
return (
<WebView
ref={webView}
source={{ uri: "https://www.talesrunnerbestguild.co.kr/" }}
style = {platformStyles.webView}
onLoadProgress={(event) => setCanGoBack(event.nativeEvent.canGoBack)}
/>
);
}
So what you can do is once your splash image appears finish then launch default View where you can show splash image for 5 seconds, which you can set timer. Then navigate to another screen whichever you want to show after your timer time end ie after 5 seconds.
As You are using expo. You can achieve it by using expo-splash-screen function SplashScreen.preventAutoHideAsync() and SplashScreen.hideAsync()
Install expo-splash-screen in your project root.
expo install expo-splash-screen
Here is what you need to add to your code. I modify it from above expo guide
export default function App() {
...
const [appIsReady, setAppIsReady] = useState(false)
async function preventSplashHide() {
try {
// Manually stop hiding the splash screen
await SplashScreen.preventAutoHideAsync()
// 5 second timer
await new Promise(resolve => setTimeout(resolve, 5000))
} finally {
setAppIsReady(true);
}
}
// call preventSplashHide() when mount
useEffect(() => {
preventSplashHide()
}, [])
const hideSplash = useCallback(async () => {
if (appIsReady) {
// hide the splash screen when root view is shown(when appIsReady is true)
await SplashScreen.hideAsync()
// meanwhile the callback is listening on appIsReady
}
}, [appIsReady])
if (!appIsReady) {
return null
}
return (
<View
onLayout={hideSplash}>
...
</View>
);
}
I am implementing firebase cloud messaging in my react native app. I have a regular class (not a react component) with static methods and variables that is instantiated when app fires. This class have three static methods/listeners (foreground, background and launch app) to manage push notifications from firebase.
I am trying to handle actions when in foreground.
How do I render a modal on top of a screen (not switching screens but run an overlay card in the center with two buttons), when user receives a push message from firebase, regardless of which screen he is on?
Tried solution provided on this article at hackernoon, wrapping modal component inside the AppContainer did not do anything. https://hackernoon.com/managing-react-modals-with-singleton-component-design-5efdd317295b
I have also tried the idea on this tutorial with modal component, but have not succeed in any cases.
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
Firebase cloud messaging class:
export default class FCM {
static appClosedListener = async () => {
const firebaseContent = await firebase.notifications().getInitialNotification();
if (firebaseContent) {
console.log(firebaseContent);
const serverParcel = JSON.parse(firebaseContent._data.payload).an;
const notificationState = "closedapp";
FCM.pushNotificationHandler(serverParcel, notificationState);
}
};
static backgroundListener = () => {
FCM.background = firebase.notifications().onNotificationOpened(firebaseContent => {
const serverParcel = JSON.parse(firebaseContent._data.payload).an;
const notificationState = "background";
FCM.pushNotificationHandler(serverParcel, notificationState);
});
};
static foregroundListener = () => {
FCM.foreground = firebase.notifications().onNotification(firebaseContent => {
const serverParcel = JSON.parse(firebaseContent._data.payload).an;
const notificationState = "foreground";
FCM.pushNotificationHandler(serverParcel, notificationState);
});
};
static pushNotificationHandler = (serverParcel, notificationState) => {
const typeOfAction = serverParcel.typeEnum;
switch(typeOfAction){
case "message":
break;
case "webnews":
const url = serverParcel.link;
NavigationService.navigate("FCMModal", {}); //Here is where I want the modal to popup.
//NavigationService.navigate("InAppBrowser", {url});
break;
}
}
App.js
const ModalStack = createStackNavigator(
{
FCMModal: FCMModal
},
{
mode: 'modal',
transparentCard: true,
cardStyle: {
// makes transparentCard work for android
opacity: 1.0
}
}
)
let rootStack = createStackNavigator(
{
main: {
screen: BottomTabNavigator //There are four navigatorstacks with multiple screens beneath here
},
InAppBrowser: {
screen: InAppBrowserStack,
defaultNavigationOptions: {
tabBarVisible: true
}
},
FCMModal: {
screen: ModalStack
}
},
{
initialRouteName: "main",
headerMode: "none"
}
);
const AppContainer = createAppContainer(rootStack);
export default class App extends Component {
render() {
return (
<AppContainer
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
);
}
}
Modal class
import React, { Component } from 'react';
import { Modal, Text, TouchableHighlight, View } from 'react-native';
export default class FCMModal extends Component {
state = {
modalVisible: true,
};
setModalVisible(visible) {
this.setState({modalVisible: visible});
}
render() {
return (
<View style={{width: 600, height: 400, borderWidth: 2, borderColor: 'red', opacity: 0.5}}>
<Modal
animationType="slide"
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => {console.log("CLOSING MODAL!")}}>
<View style={{marginTop: 22}}>
<View>
<Text>Hello World!</Text>
<TouchableHighlight
onPress={() => {
this.setModalVisible(false);
}}>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
</View>
);
}
}
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;
I am trying get re render a screen every time the user presses a tab or moving from one tab to another and then to first tab. I also placed a custom header in the screen. Same header on all the other tabs too getting some state from Asynchronous storage but it is also not refreshing once the screen has loading. I am using react navigation. Is there any method in navigation which will be called whenever tab is focused.
const AppStack = createBottomTabNavigator(
{
Search: SearchScreen,
Saved: SavedScreen,
Explore: ExploreStack,
Offers: OffersScreen,
Profile: ProfileScreen,
},
{
initialRouteName: 'Explore',
navigationOptions: ({navigation})=>({
tabBarIcon: ({focused, tintColor})=>{
const { routeName } = navigation.state;
let iconName, iconSize;
switch(routeName) {
case "Search":
iconName = `ios-search${focused ? '' : '-outline'}`;
break;
case "Saved":
iconName = `ios-heart${focused ? '' : '-outline'}`;
break;
case "Explore":
iconName = `ios-navigate${focused ? '' : '-outline'}`;
break;
case "Offers":
iconName = `ios-pricetag${focused ? '' : '-outline'}`;
break;
case "Profile":
iconName = `ios-person${focused ? '' : '-outline'}`;
break;
default:
break;
}
return <Icon name={iconName} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: 'black',
inactiveTintColor: 'grey',
activeBackgroundColor: '#abaf9b',
labelStyle: {
fontSize: 15,
},
// style for tabbar
style: {
backgroundColor: '#ffffff',
height: 60,
justifyContent: 'space-around',
alignContent: 'center',
alignItems: 'center',
},
// style for tab
tabStyle: {
paddingTop: 7,
paddingBottom: 7
}
},
}
)
This is one of the tab. Other tabs a very similar using same component but the different apis.
import React, { Component } from 'react';
import { View, Image, ActivityIndicator, TouchableWithoutFeedback, TouchableHighlight, AsyncStorage } from 'react-native';
import HeaderComponent from '../components/Header';
import SomeComponent from '../components/Some';
import { Container, Content, Icon, Spinner} from 'native-base';
class FirstScreen extends Component{
constructor(props){
super(props)
this.state = {
somelist: [],
name: '',
userId: '',
isloading: true,
location: ''
};
this.getUser();
}
componentDidMount(){
this.getLocation();
}
getLocation = async() => {
const result = await AsyncStorage.getItem('location');
console.log("Location " +result)
this.setState({location: result});
}
getUser = async() => {
const result = await AsyncStorage.getItem('user');
const data = JSON.parse(result);
console.log("data : "+data)
this.setState({name: data.name, userId: data.userId})
}
componentWillMount(){
console.log("Component will mount")
//For demo
fetch('http://someapi.co/api/listing')
.then(response => response.json())
.then(data => {
this.setState({ somelist: data, isloading: false }, function(){console.log(this.state.somelist, this.state.isloading)})
})
.catch(function(error){
console.log("Error : "+error);
});
//console.log(this.state.barlist);
}
renderComponent(){
if(this.state.isloading == true){
return (
<View style={{ flex: 1, justifyContent: 'center', height: 300 }}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
);
}
return this.state.somelist.map( user=>
<SomeComponent key={user.id}
onPress={()=>this.props.navigation.navigate('OtherScreen', {'Id': user.id, 'userId': this.state.userId})}
image={user.image[0].imageName}
/>
);
}
render(){
console.log(this.state.userId)
return (
<Container>
<HeaderComponent
profilePress={()=>this.props.navigation.navigate('Profile')}
seachPress={()=>this.props.navigation.navigate('SearchSetting')}
// location={this.state.location}
/>
<TouchableHighlight
style={{position: 'absolute', bottom: 20, zIndex:999999, right: 20 }}
onPress={()=>this.props.navigation.navigate('Map')}
>
<Image source={require('../images/navigation_icon.png')} style={{height: 50, width: 50}}/>
</TouchableHighlight>
<Content>
<View style={{padding: 0, margin: 0}}>
{this.renderComponent()}
</View>
</Content>
</Container>
);
}
}
export { SomeScreen };
You can access the event listeners in react-navigation as mentioned here
// call when the screen is focused
componentDidMount () {
this._onFocusListener = this.props.navigation.addListener('didFocus', (payload) => {
// refresh the component here
// or update based on your requirements
});
}
Please refer the link: https://reactnavigation.org/docs/en/navigation-prop.html#addlistener-subscribe-to-updates-to-navigation-lifecycle
There are 2 different ways I found below,
// call when the screen is focused
componentDidMount () {
this._navListener = this.props.navigation.addListener('didFocus', (payload) => {
// update based on your requirements
});
}
OR
import { NavigationEvents } from "react-navigation";
...
class HomeScreen extends React.Component {
render() {
return (
<View>
<NavigationEvents
onWillFocus={() => {
// update based on your requirements!
}}
/>
<Text>Home</Text>
</View>
);
}
}