React-Native "expected a component class, got object Object" - reactjs

I have a big code that does not run because of the error in the headline.
This is one file of my code, can anyone see what is wrong? I believe it is the line "{this.state.todos.map((todo,index) =>" (code line 62)
I have capitalised the name of the objects, so that is not the issue (I think).
npm -v 4.6.1
import React, { Component } from 'react';
//import $ from 'jquery';
import { Button, View, FormGroup, FormControl, ControlLabel } from 'react-native';
import { Text } from 'react-native-svg'
/* generating sample data to be shown, these data names are used to access the values*/
var todos = [];
//Will not work first time, since list do not exist in AsyncStorage.
//Get from AsyncStorage.
try{
todos = JSON.parse(AsyncStorage["todos"]);
}catch(ex){
console.log("Not working" + ex);
todos = [];
}
//Errormessage for errorhandeling.
var errorMessage = "";
/*--------------------*/
class Todos extends Component {
constructor(props) {
super(props);
this.state = {
todos
};
this.handleAddTodo = this.handleAddTodo.bind(this);
}
handleAddTodo(todo) {
/* creates todo in list that shows*/
this.setState({todos: [...this.state.todos, todo]});
/*this code saves in AsyncStorage*/
todos.push(todo);
//AsyncStorage...
AsyncStorage.setItem("todos", JSON.stringify(todos));
}
/* function that removes todos from the list*/
handleRemoveTodo(index) {
this.setState({
todos: this.state.todos.filter(function(e, i) {
return i !== index;
})
})
/* now working as expected*/
todos.splice(index, 1);
AsyncStorage.setItem("todos", JSON.stringify(todos));
}
render() {
return (
<View>
<TodosInput onAddTodo={this.handleAddTodo} />
<hr />
<Text>todo count: <span>{this.state.todos.length}</span></Text>
<View>
<View>{this.state.todos.map((todo,index) =>
<View key={index}>
<Text style={style.list-group-item-heading}>{todo.todoTitle}<small><span style={style.label} label-info></span></small> </Text>
<View>{todo.todoDesc}</View>
<Button bsStyle="danger" onClick={this.handleRemoveTodo.bind(this, index)}><span style={style.glyphicon} glyphicon-trash></span></Button>
</View>
)}</View>
</View>
</View>
);
}

It is more likely because of using html tags instead of native components.
Like hr span and small.
So you need to create your own components and also titlecase their names.
Here is possible example how to fix your problem:
const Small = ({ children }) => (
<Text
style={{
fontSize: 10
}}
>
{children}
</Text>
);
const HR = () => (
<View
style={{
height: 1,
borderBottomColor: "black",
borderBottomWidth: 1
}}
/>
);
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<View>
<Text>This is span</Text>
</View>
<View>
<Text>
Regular test <Small>With {"<small>"} text</Small> Inside
</Text>
</View>
<View>
<Text>This is {"<HR />"} bellow</Text>
<HR />
</View>
</View>
);
}
}
UPD: Actually there are a lot of other bugs in a code above
Quick list of possible issues:
as pointed dalready - replace all html tags with native components
style={style.list-group-item-heading} You cannot use dash separator in js except ni a strings - possible fix: style={style['list-group-item-heading']}
<View>{todo.todoDesc}</View> - you have to wrao text with Text compnent
<Button /> component have required prop title it should be a string, as well as it dosn't have OnClick handler but requires OnPress
To summarize - i would note that react native uses jsx but it is not html. It have its own API and you'll have to grasp it carefully.

Related

How to Get data from react native-deck-swiper when swiped

I am using react native deck swipper to display arrays of data.
//ARRAYS OF DATA
const docs = shuffleArray([
{
title: "Austin Wade",
content: 22,
featured_image: require("../../assets/images/beach.jpeg"),
created_at: "2020-11-11T16:26:13.000000Z",
news_url: "https://www.google.com",
key: "caseex6qfO4TPMYyhorner",
},..... more json arrays...])
My problem is, i want to be able to extract the news_url when the card is swipped left, and also use the extracted URL to open expo inapp-browser that will display the webpage e.g www.google.com
I have written a function that opens a web browser.
PLEASE CAN SOMEONE HELP ME
//SWIPPER
<SafeAreaView style={{ flex: 1 }}>
{/* <View style={{ flex: 1, padding: 16 }}> */}
<Swiper
ref={useSwiper}
cards={docs}
cardIndex={0}
backgroundColor="transparent"
stackSize={2}
showSecondCard
cardHorizontalMargin={0}
animateCardOpacity
disableBottomSwipe
renderCard={
((card) => <Cardz card={card} />)
}
onSwiped={(cardIndex) => {
console.log(cardIndex);
}}
onSwipedAll={() => {
console.log("onSwipedAll");
}}
onSwipedTop={() => {
console.log(getLatestNews);
}}
onSwipedBottom={() => {
// <Toast message={success} onDismiss={() => {}} />
}}
//swipping left, opens expo web browser
onSwipedLeft={() => {
_handleWebBrowserAsync(getNewsUrl);
//Alert.alert();
}}
></Swiper>
{/* </View> */}
</SafeAreaView>
);
//WEB BROSWER
//async function to open app inapp web browser
const _handleWebBrowserAsync = async (url) => {
try {
_addLinkingListener();
await WebBrowser.openBrowserAsync(url);
//only calls this method in IOS Devices as it only
//works for IOS Devices
if (Constants.platform.ios) {
_removeLinkingListener();
}
} catch (error) {
Alert.alert("Error:", error.message);;
console.log("Error:" + error.message);
}
};
//CARD COMPONENT
import React from 'react'
import { View, Text, Image, ImageSourcePropType } from 'react-native'
import styles from './Card.styles'
const Cardz = ({ card }) => (
<View activeOpacity={1} style={styles.card}>
<Image
style={styles.image}
source={card.featured_image}
resizeMode="cover"
/>
<View style={styles.photoDescriptionContainer}>
<Text style={styles.text}>{`${card.title}, ${card.content}`}</Text>
</View>
</View>
);
export default Cardz
There are event callbacks.
For example: There is onSwiped prop which is a
function to be called when a card is swiped. it receives the swiped card index.
So, you'll get that index value which you'll use to get the object from the docs array. If the swiped index is 2, you can get the object like this: docs[2].

InfiniteScroll using ScrollView - React Native

I have a list using the ScrollView of react native itself. Basically, I build a list dynamically through an API return.
async fetchData(userSearch) {
const {route} = this.props;
const {params} = route;
const {type} = params;
this.setState({
loading: true,
});
const responseProcedures = await scheduleResource.getProcedures(userSearch);
this.setState({
procedures: responseProcedures.data,
loading: false,
});
}
<ScrollView
onScroll={(event) => this.shouldLoadMoreContent(event)}
>
{procedures.map(procedure => (
<ArrowBox key={procedure.id} onPress={() => RootNavigation.navigate('ProcedureDetails', {procedure})}>
<Text bold style={styles.ProcedureTitle}>
{procedure.name}
</Text>
{!!procedure.synonyms.length && (
<>
<Text bold style={styles.ProcedureSynonymTitle}>
SinĂ´nimos
</Text>
<View style={styles.ProcedureSynonymOptionsContainer}>
{procedure.synonyms.map(synonym => <Text style={styles.ProcedureSynonymOption} key={synonym}>{synonym}</Text>)}
</View>
</>
)}
</ArrowBox>
))}
</ScrollView>
The problem is that I load the entire return from the API and it slows down.
I would like to know how to dynamically load the content and make new calls in the api, when I reach the end of the page.
Api allows me to place offset and limit.
Could someone give me some example?
Thanks!!!!!
Basically the ScrollView is not designed to handle dynamic data, the correct component that is designed to perform this function is called Flatlist. It works almost exactly like ScrollView but it is faster and will only render components that are shown on the screen.
Please import Flatlist from React Native like this...
//At the top of your file, please import FlatList together with all the modules that you want
import { FlatList, Text, View } from "react-native";
Then replace the entire ScrollView in your code with a Flatlist like this:
<FlatList
keyExtractor={(procedure) => procedure.id}
data={this.state.procedures}
renderItem={(procedure) => {
return (
<ArrowBox
key={procedure.id}
onPress={() =>
RootNavigation.navigate("ProcedureDetails", {
procedure })}>
<Text bold style={styles.ProcedureTitle}>
{procedure.name}
</Text>
{!!procedure.synonyms.length && (
<>
<Text bold style={styles.ProcedureSynonymTitle}>
SinĂ´nimos
</Text>
<View
style={styles.ProcedureSynonymOptionsContainer}>
{procedure.synonyms.map((synonym) => (
<Text
style={styles.ProcedureSynonymOption}
key={synonym}>
{synonym}
</Text>
))}
</View>
</>
)}
</ArrowBox>
);
}}
></FlatList>;

React native re-render causes view to scroll to top - Do my keys change between renders?

I am using react native, redux and flatlist. I render 10 items and then fetch / render the next 10 items on click (load more). My problem is that when I use setState(to increase the page counter) (thats why I dont use setstate in my code) to fetch more items (i use pagination) or ask whether or not Im fetching (true when fetching -> shown loading sign, false when fetched -> showing items), that triggers
a re-render and my view gets scrolled to top.
Ive been reading for a while now and event.preventDefault (as sometimes suggested) doesnt work and I do believe that my problem is the key / parent tree of the corresponding element changes between renders. I do give each item the key of its ID which is always unique. Am I doing it wrong? Or what else could be causing this? Thanks!
class App extends React.Component {
state = {
page : 0
}
componentDidMount() {
this.props.fetchData(0)
this.state.page++;
}
load = () => {
this.props.fetchData(this.state.page);
this.state.page++;
}
renderRow = ({item}) => {
return (
<Item
key={item.id}
coin={item}
/>
)
}
renderButton() {
return <Button
title="Load more"
onPress={this.load}
/>
}
render() {
if (this.props.isFetching) {
return (
<View>
<Text>Loading</Text>
</View>
)
}
return (
<View style={styles.container}>
<FlatList
style={{width: '100%'}}
data={this.props.data}
renderItem={this.renderRow.bind(this)}
ListFooterComponent={this.renderButton.bind(this)}
/>
</View>
);
}
}
mapStateToProps = state => {
return {
isFetching: state.data.isFetching,
data: state.data.data
}
}
export default connect(mapStateToProps, {fetchData})(App)
Hey change your render method return like below :
return (
{ this.state.isFetching && <View> <Text>Loading</Text> </View>}
<View style={styles.container}>
<FlatList
style={{width: '100%'}}
data={this.props.data}
renderItem={this.renderRow.bind(this)}
ListFooterComponent={this.renderButton.bind(this)}
/>
</View>
);

React Native "onViewableItemsChanged" not working while scrolling on dynamic data

I have a React Native FlatList.
base on Documentation I used onViewableItemsChanged for getting the current showing item on the screen. but while scrolling or when scrolling stops nothing happens and I can't get the current Id.
How Can I use it in correct way?
export default class TimeLine extends Component {
constructor(props) {
super(props);
this.renderTimeline = this.renderTimeline.bind(this);
this.state = {
timeline: [],
};
this.handleViewableItemsChanged = this.handleViewableItemsChanged.bind(this);
this.viewabilityConfig = {
itemVisiblePercentThreshold: 50,
};
}
...
renderTimelineList(timeline_data) {
return (
<Card key={timeline_data.unique_id}>
<CardItem style={styles.header}>
<Left>
<Icon type="MaterialIcons" name={this.timelineIcon(timeline_data.type)}/>
<Body>
<Text style={styles.header_title}>{timeline_data.title}</Text>
</Body>
</Left>
</CardItem>
<CardItem>
{this.renderTimeline(timeline_data)}
</CardItem>
<CardItem>
<Left>
<Icon small name="time" style={styles.small_font}/>
<Text note style={styles.small_font}>{timeline_data.release_date}</Text>
</Left>
<Right>
<Text note style={styles.small_font}>{timeline_data.duration}</Text>
</Right>
</CardItem>
</Card>
);
}
render() {
return (
<Container>
<Content>
<FlatList
key={this.state.key}
onViewableItemsChanged={this.handleViewableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
data={this.state.timeline}
renderItem={({item}) => this.renderTimelineList(item)}
/>
</Content>
</Container>
);
};
}
Make sure that you implement keyExtractor correctly (or implement it if you haven't already).
In my case keyExtractor was returning null for many children, and onViewableItemsChanged would not dispatch for those.
I solved the problem myself.
The problem was because of I was trying to populate the data with Ajax.
To solve that I ran the request from the Parent component and passed it in props
and inside the constructor of the class if filled the state for my array.
Another thing that you should consider is that you should render the whole FlatList inside a View.
here is my final code:
export default class TimeLine extends Component {
constructor(props) {
super(props);
this.viewabilityConfig = {
minimumViewTime: 500,
viewAreaCoveragePercentThreshold: 60,
};
}
render() {
const {timeline} = this.state;
return (
<View style={styles.container}>
<FlatList
data={timeline}
renderItem={({item}) => this.renderTimelineList(item)}
viewabilityConfig={this.viewabilityConfig}
onViewableItemsChanged={({viewableItems}) => this.handleViewableItemsChanged(viewableItems)}
/>
</View>
);
};
}

FlatList calling twice

I have this code
class Home extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: []
}
this._handleRenderItem = this._handleRenderItem.bind(this);
this._keyExtractor = this._keyExtractor.bind(this);
}
componentDidMount() {
let success = (response) => {
this.setState({ dataSource: response.data });
};
let error = (err) => {
console.log(err.response);
};
listarProdutos(success, error);
}
_keyExtractor = (item, index) => item._id;
_handleRenderItem = (produto) => {
return (
<ItemAtualizado item={produto.item} />
);
}
render() {
return (
<Container style={styles.container}>
<Content>
<Card>
<CardItem style={{ flexDirection: 'column' }}>
<Text style={{ color: '#323232' }}>Produtos atualizados recentemente</Text>
<View style={{ width: '100%' }}>
<FlatList
showsVerticalScrollIndicator={false}
data={this.state.dataSource}
keyExtractor={this._keyExtractor}
renderItem={this._handleRenderItem}
/>
</View>
</CardItem>
</Card>
</Content>
</Container>
);
}
}
export default Home;
The function _handleRenderItem() is being called twice and I can't find the reason. The first time the values inside my <ItemAtualizado /> are empty, but the second was an object.
This is normal RN behavior. At first, when the component is created you have an empty DataSource ([]) so the FlatList is rendered with that.
After that, componentDidMount triggers and loads the updated data, which updates the DataSource.
Then, you update the state with the setState which triggers a re render to update the FlatList.
All normal here. If you want to try, load the datasource in the constructor and remove the loading in the componentDidMount. It should only trigger once.
If you want to control render actions, you can use shouldComponentUpdate method.
For example:
shouldComponentUpdate(nextProps, nextState){
if(this.state.friends.length === nextState.friends.lenght)
return false;
}
it will break render if friends count not change.
I recreated the issue in this snack. https://snack.expo.io/B1KoX-EUN - I confirmed you can use shouldComponentUpdate(nextProps, nextState) to diff this.state or this.props and return true/false - https://reactjs.org/docs/react-component.html#shouldcomponentupdate docs say this callback should be used only for optimization which is what we're doing here.

Resources