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;
Related
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");
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>
This is the tutorial's link(https://medium.com/react-native-development/how-to-use-the-flatlist-component-react-native-basics-92c482816fe6) from where I'm learning react-native.
Everything is working fine except the view is not scrolling
import React, { Component } from 'react';
import { FlatList, Text, View } from 'react-native';
import { List, ListItem } from "react-native-elements";
type Props = {}
export default class FlatListDemo extends Component<Props> {
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
})
)
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
>
</View>
)
};
render() {
return (
<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 }}
/>
)}
ItemSeparatorComponent={this.renderSeparator}
onEndReachedThreshold={50}
/> )
}}
Is it compulsory to use ScrollView to make able to scroll react-native view or we can do it
using any CSS or something else
When I tried to run this code in this given link: https://snack.expo.io/rkOsCaiD7 to find for a solution what I found was scroll view was working fine with the above code. Just try to reload and run it in react native by which I feel it might resolve your issue.
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>
);
}
}
I've tried flatlist but it has a bit of performance issues in android.
As I scroll down, it loads the list. But afterwards, it shows blank while scrolling upwards.
After reaching the end of the screen, it stops for a while and then loads the datas. Why is it not showing loader (activity indicator) at the bottom? Why is onEndReached and onEndReachedThreshold not working?
Plz have a look at the video here
https://youtu.be/5tkkEAUEAHM
My code:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
FlatList,
ActivityIndicator,
} from 'react-native';
import { List, ListItem, SearchBar } from "react-native-elements";
export default class FlatListExample 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`;
console.log('url', url);
this.setState({ loading: true });
setTimeout(()=>{
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({ error, loading: false });
});
},0);
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
handleLoadMore = () =>{
this.setState({
page:this.state.page + 1,
},()=>{
this.makeRemoteRequest();
})
}
render() {
return (
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
avatar={{ uri: item.picture.thumbnail }}
/>
)}
keyExtractor={item => item.email}
ListFooterComponent={this.renderFooter}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={50}
/>
);
}
}
AppRegistry.registerComponent('FlatListExample', () => FlatListExample);
I've noticed that you're not setting initialNumToRender. From the docs:
initialNumToRender: number
How many items to render in the initial batch. This should be enough
to fill the screen but not much more. Note these items will never be
unmounted as part of the windowed rendering in order to improve
perceived performance of scroll-to-top actions.
So you'll want to estimate how many cells you expect to be visible at any given time and set it to that. I'd also recommend if you haven't already to update to the latest react-native which includes various improvements on the FlatList component.