using classes and rendering api call in React Native - react-native

I am totally new in React Native, I am having problem rendering data from API call. When I do it inside function it is working for me when I am using useEffect... but in the Class I cannot use that.
Here is example of my code...
export default class Categories extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
};
}
componentDidMount() {
this.regionsGetRequest();
}
regionsGetRequest = () => {
this.setState({ loading: true });
const fetchData = async () => {
const result = await axios(
'https://...........json'
);
this.setState({
data: result.data,
error: result.error || null,
loading: false,
});
};
fetchData();
};
renderCategories = () => {
const categoryLive = this.state.data;
console.log(JSON.stringify(categoryLive));
I am getting in console: undefined, undefined... and then results as they should... like it is running 3 times for some reason... if I try to put above renderCategories:
componentDidMount() {
renderCategories();
}
I am getting just one undefined... when I connect variable categoryLive nothing is loading.
Sorry I have been strugling with this one... any help is really appreciated!!
No matter what I do, I am always getting 3 calls... first two empty object [], and third I get real results dumped in console. So my categories are not rendering.. they are empty.
Here is updated code, and I am posting whole file, it might ring some bells.
export default class Categories extends React.Component {
state = {
myData: [],
};
componentDidMount() {
axios
.get('https://..............json')
.then((res) => {
const myData = res.data;
this.setState({ myData });
});
}
renderCategories = () => {
const categoryLive = this.state.myData;
console.log(JSON.stringify(categoryLive));
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const categories = tabId
? categoryLive[tabId]
: categoryLive.victoria_bc;
//console.log(route.params?.tabId);
return (
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.categoryList}
>
<Block flex>
{categories.map((category) => (
<TouchableWithoutFeedback
key={`category-${category.id}`}
onPress={() => navigation.navigate('Category', { ...category })}
>
<Block flex card style={[styles.category, styles.shadow]}>
<ImageBackground
source={{ uri: category.image }}
style={[
styles.imageBlock,
{ width: width - theme.SIZES.BASE * 2, height: 252 },
]}
imageStyle={{
width: width - theme.SIZES.BASE * 2,
height: 252,
}}
>
<Block style={styles.categoryTitle}>
<Text size={18} bold color={theme.COLORS.WHITE}>
{category.title}
</Text>
</Block>
</ImageBackground>
</Block>
</TouchableWithoutFeedback>
))}
</Block>
</ScrollView>
);
};
render() {
return (
<Block flex center style={styles.categories}>
{this.renderCategories()}
</Block>
);
}
}
When I put it like this: I am getting default category ( and all data just fine... ) but my navigation is not working any more... (route.params?.tabId is not updating)
axios
.get('https://.............json')
.then((res) => {
this.setState({
myData: res.data,
error: res.error || null,
loading: false,
});
console.log('inside .then----' + JSON.stringify(this.state.myData));
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const tmpCategories = tabId
? this.state.myData[tabId]
: this.state.myData.victoria_bc;
this.setState({ categories: tmpCategories });
//console.log(route.params?.tabId);
});
If I put it like this as below... category is empty for me:
axios
.get('https://.............json')
.then((res) => {
this.setState({
myData: res.data,
error: res.error || null,
loading: false,
});
console.log('inside .then----' + JSON.stringify(this.state.myData));
});
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const tmpCategories = tabId
? this.state.myData[tabId]
: this.state.myData.victoria_bc;
this.setState({ categories: tmpCategories });
//console.log(route.params?.tabId);
Final code that is working for me..
export default class Categories extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
myData: [],
error: null,
};
}
componentDidMount() {
this.renderCategories();
}
renderCategories = () => {
axios
.get('https://.............json')
.then((res) => {
this.setState({
myData: res.data,
error: res.error || null,
loading: false,
});
//console.log('inside .then----' + JSON.stringify(this.state.myData));
});
};
render() {
if (this.state.loading) {
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}
>
<ActivityIndicator />
</View>
);
} else {
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const categories = tabId
? this.state.myData[tabId]
: this.state.myData.victoria_bc;
//console.log(route.params?.tabId);
return (
<Block flex center style={styles.categories}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.categoryList}
>
<Block flex>
{categories.map((category) => (
<TouchableWithoutFeedback
key={`category-${category.id}`}
onPress={() =>
navigation.navigate('Category', { ...category })
}
>
<Block flex card style={[styles.category, styles.shadow]}>
<ImageBackground
source={{ uri: category.image }}
style={[
styles.imageBlock,
{ width: width - theme.SIZES.BASE * 2, height: 252 },
]}
imageStyle={{
width: width - theme.SIZES.BASE * 2,
height: 252,
}}
>
<Block style={styles.categoryTitle}>
<Text size={18} bold color={theme.COLORS.WHITE}>
{category.title}
</Text>
</Block>
</ImageBackground>
</Block>
</TouchableWithoutFeedback>
))}
</Block>
</ScrollView>
</Block>
);
}
}
}

export default class Categories extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
myData: [],
error: null,
categories: []
};
}
componentDidMount() {
renderCategories();
}
renderCategories = () => {
this.setState({ loading: true });
axios
.get('https://..............json')
.then((res) => {
this.setState({ myData: res.data,
error: res.error || null,
loading: false
});
const categoryLive = this.state.myData;
console.log(JSON.stringify(categoryLive));
});
};
render() {
// Set params in your render method :
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
if (this.state.loading){
return (
<View style={{ flex : 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator/>
</View>
);
}
return (
// assign in your return statement
this.setState({ categories: tabId
? categoryLive[tabId]
: categoryLive.victoria_bc;})
//console.log(route.params?.tabId);
const { categories } = this.state.categories;
<Block flex center style={styles.categories}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.categoryList}
>
<Block flex>
{categories.map((category) => (
<TouchableWithoutFeedback
key={`category-${category.id}`}
onPress={() => navigation.navigate('Category', { ...category })}
>
<Block flex card style={[styles.category, styles.shadow]}>
<ImageBackground
source={{ uri: category.image }}
style={[
styles.imageBlock,
{ width: width - theme.SIZES.BASE * 2, height: 252 },
]}
imageStyle={{
width: width - theme.SIZES.BASE * 2,
height: 252,
}}
>
<Block style={styles.categoryTitle}>
<Text size={18} bold color={theme.COLORS.WHITE}>
{category.title}
</Text>
</Block>
</ImageBackground>
</Block>
</TouchableWithoutFeedback>
))}
</Block>
</ScrollView>
</Block>
);
}
}

Related

Searching in Expo's FlatList

I used this tutorial but it didn't work (if you are interested, check out my last post). Any suggestions to make a working search for a flatlist?
I have a list of 100 things and just by inserting the name in a search bar, the flatlist should update with the results.
Try using react-native-searchable-flatlist
import React, { Component } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import { ListItem, SearchBar } from 'react-native-elements';
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
};
this.arrayholder = [];
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `https://randomuser.me/api/?&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%',
}}
/>
);
};
searchFilterFunction = text => {
this.setState({
value: text,
});
const newData = this.arrayholder.filter(item => {
const itemData = `${item.name.title.toUpperCase()} ${item.name.first.toUpperCase()} ${item.name.last.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
data: newData,
});
};
renderHeader = () => {
return (
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => this.searchFilterFunction(text)}
autoCorrect={false}
value={this.state.value}
/>
);
};
render() {
if (this.state.loading) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
leftAvatar={{ source: { uri: item.picture.thumbnail } }}
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
/>
</View>
);
}
}
export default FlatListDemo;

Use global variable to generate a dynamic url (React Native)

I have a datapicker where I select the country and with this I create a url.
<View style={styles.centrado}>
<Text style={styles.seleccion}>Elige tu País</Text>
{this.Paises()}
<Picker
selectedValue={this.state.value || ""}
style={{ height: 50, width: 120 }}
itemStyle={styles.seleccion}
onValueChange={(value, idx) => {
this.setState({ value, idx });
global.Pais = value;
console.log({ valor: value });
this.props.navigation.navigate("Noticias", {
pais: value
});
}}
>
<Picker.Item label="Seleccione" value="" />
<Picker.Item label="Argentina" value="ar" />
<Picker.Item label="Bolivia" value="bo" />
</Picker>
</View>
In the first load everything works but when I return to the home and select another country the global (global.Pais) variable remains with the initial value.
export default class noticias extends React.Component {
state = {
loading: true,
noticias: []
};
constructor(props) {
super(props);
this.fetchNoticias();
}
fetchNoticias = async () => {
console.log(global.Pais);
if (!global.Pais || global.Pais == "undefined") {
alert("Selecciona un país para ver las noticias");
this.props.navigation.navigate("Home");
} else if (global.Pais == "uy") {
const response = await fetch(
`https://prueba.${global.Pais}/wp-json/wp/v2/posts`
);
const res = await response.json();
this.setState({ noticias: res, loading: false });
} else {
const response = await fetch(
`https://prueba.com.${global.Pais}/wp-json/wp/v2/posts`
);
const res = await response.json();
this.setState({ noticias: res, loading: false });
}
};
componentDidMount() {
this.fetchNoticias();
}
render() {
const { loading, noticias } = this.state;
if (loading) {
return (
<View style={styles.container}>
<Text>Cargando .....</Text>
</View>
);
}
return (
<View>
<FlatList
data={this.state.noticias}
keyExtractor={(x, i) => i.toString()}
renderItem={({ item }) => (
<View>
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("Noticia", {
post_id: item.id
})
}
>
<Card
style={{
shadowOffset: { width: 5, height: 5 },
width: "90%",
borderRadius: 12,
alignSelf: "center",
marginBottom: 10
}}
>
<Card.Content>
<Title>{item.title.rendered}</Title>
</Card.Content>
<Card.Cover
source={{
uri:
item.better_featured_image.media_details.sizes.medium
.source_url
}}
/>
<Card.Content>
<HTMLRender html={item.excerpt.rendered} />
</Card.Content>
</Card>
</TouchableOpacity>
</View>
)}
/>
</View>
);
}
}
How can I solve it?
I think that won't work, you could use react-context or redux to save and update that value or
this.props.navigation.setParams({ pais: value })
and then get that value when you need it
this.props.navigation.getParam('pais')

React Native Pass Index to Props

I have a modal that contain icons and description and status, and i want to pass the icons and descriptions from index to the modal,I already pass the status. is there anyway to do that? sorry i'm still new to react native and thanks in advance
this is my index.js
export const img =
{
itemStatus: {
"Open": { name: 'open-book', type: 'entypo', color: '#ffb732', desc:'New Attribut, New Attention'},
"Approved": { name: 'checklist', type: 'octicon', color: '#3CB371', desc:'Approved by SPV/MNG' },
"Escalated": { name: 'mail-forward', type: 'font-awesome', color: '#ffb732', desc:'Escalated to SPV/MNG' },
"Deliver Partial": { name: 'arrange-send-to-back', type: 'material-community', color: '#8B4513', desc:'Some items in a DO have not arrived/was faulty' },
};
and this is my container
class MyRequest extends React.Component {
constructor() {
super();
this.state = {
currentStatus: null,
refreshing: false,
fetchStatus: null
};
handleShowModal = (status) =>{
this.setState({
currentStatus: status,
});
}
handleDismissModal = () =>{
this.setState({currentStatus: null});
}
<View style={[styles.panelContainer, status === 'success' ? {} : { backgroundColor: color.white }]}>
<FlatList
showsVerticalScrollIndicator={false}
progressViewOffset={-10}
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
onMomentumScrollEnd={(event) => event.nativeEvent.contentOffset.y === 0 ? this.onRefresh() : null}
data={content}
renderItem={({ item }) => item}
keyExtractor={(item, key) => key.toString()}
/>
</View>
<IconModal visible={this.state.modalVisible} close={this.handleDismissModal} icon={} status={this.state.currentStatus} desc={} />
}
and this is my modal
const IconModal = (props) => {
return(
<Modal
isVisible={props.visible}
onBackdropPress={props.close}
>
<View style={styles.dialogBox}>
<View style={styles.icon}>
<Icon>{props.icon}</Icon>
</View>
<View style={styles.text}>
<Text style={styles.status}>{props.status}</Text>
<Text>{props.desc}</Text>
</View>
<TouchableOpacity onPress={props.close}>
<View>
<Text style={styles.buttonText}>GOT IT</Text>
</View>
</TouchableOpacity>
</View>
</Modal>
)
}
It's a bit unclear how you plan on mapping against img.itemStatus index but you can just reference the object you want as such.
import img from '....path_to_index.js'
...
// const currentItemStatus = img.itemStatus.Open
// OR
const itemStatus = 'Open' // Or 'Approved', 'Escalated', 'Deliver Partial'
const currentItemStatus = img.itemStatus[itemStatus]
...
<IconModal
visible={this.state.modalVisible}
close={this.handleDismissModal}
icon={currentItemStatus.name} // Passing name
status={this.state.currentStatus}
desc={currentItemStatus.desc} // Passing desc
/>
...
Hope this was helpful

React Native Invariant Violation

I've been following a guide about making use of FlatLists. I have copied code from this GitHub repo: https://github.com/ReactNativeSchool/react-native-flatlist-demo/blob/master/FlatListDemo.js
I'm getting an issue however...
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s%s, 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.
Here is my code:
import React, { Component } from "react";
import { View, FlatList, ActivityIndicator } from "react-native";
import { List, ListItem, SearchBar } from "react-native-elements";
class SettingsScreen 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 SettingsScreen;
Through looking at sources online I can see it is probably my imports that are breaking the code but I can't see it myself. Any suggestions?
Thanks

React Native Trying to search for users, refreshing every time the user types

I'm trying to make an auto-refreshing flatlist every time the user types something, like Instagram’s search does.
The compiler keeps complaining about a missing variable called search.
import React, { Component } from "react";
import { View, Text, FlatList, ActivityIndicator } from "react-native";
import { List, ListItem, SearchBar } from "react-native-elements";
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
error: null,
search: '',
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const { page, search } = this.state;
const url = `https://pacific-basin-57759.herokuapp.com/api/account/users?page=${page}&search=${search}`;
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,
search: this.state.search
})
})
.catch(error => {
this.setState({ error, loading: false });
});
};
handleLoadMore = () => {
this.setState(
{
page: this.state.page + 1
},
() => {
this.makeRemoteRequest();
}
);
};
handleSearch = () => {
this.setState(
{
search: this.state.search
},
() => {
this.makeRemoteRequest();
}
);
}
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
renderHeader = () => {
return <SearchBar placeholder="Type Here..." lightTheme round onChangeText={(text) => this.setState({search:text})} />;
};
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.first_name} ${item.last_name}`}
subtitle={item.username}
//avatar={{ uri: item.picture.thumbnail }}
containerStyle={{ borderBottomWidth: 0 }}
/>
)}
keyExtractor={item => item.id}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
/>
</List>
);
}
}`
I've looked at the Fetch API documentation at MDN and it doesn't really give any useful info (it's all over the place).