How to display array elements in react native using FlatList - react-native

I'm trying to fetch data from an API and display that in a FlatList component in react. But when displaying data in the array, it displays an empty screen. Below is my code
useEffect(() => {
axios.get(`https://api.github.com/users/${user}/repos`)
.then(response => {
console.log(response.data)
response.data.map(repo => {
let rep = {
id:repo.id,
name:repo.name,
created:repo.created_at,
url:repo.html_url
}
repos.push(rep)
})
console.log(repos)
})
.catch(err => {
setError(true)
console.log(err)
})
},[])
return (
<View>
<Header navigate={navigation}/>
<FlatList
contentContainerStyle={{backgroundColor:'grey',marginTop:25}}
data={repos}
renderItem={({repo}) => (
<Text>
{repo.name}
</Text>
)}
/>
</View>
)

Here is the full working example: Expo Snack
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
import Constants from 'expo-constants';
import axios from 'axios';
export default function App() {
const [repos, setRepos] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
axios
.get(`https://api.github.com/users/theketan2/repos`)
.then((response) => {
let data = [];
//console.log(response.data);
response.data.map((repo) => {
let rep = {
id: repo?.id,
name: repo?.name,
created: repo?.created_at,
url: repo?.html_url,
};
data.push(rep);
});
// console.log(data);
setRepos(data);
})
.catch((err) => {
setError(true);
console.log(err);
});
}, []);
useEffect(() => {
console.log(repos);
}, [repos]);
return (
<View style={styles.container}>
<FlatList
data={repos}
renderItem={({item}) => (
<View style={styles.list}>
<Text>{item?.name}</Text>
</View>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'rgba(21,21,21,0.1)',
},
list: {
padding: 10,
marginTop: 5,
borderRadius: 5,
backgroundColor: 'white',
marginHorizontal: 5,
},
});

Try this way
const [data, setData] = useState([]);
useEffect(() => {
axios.get(`https://api.github.com/users/${user}/repos`)
.then(response => {
setData(response.data);
})
.catch(err => {
setError(true)
console.log(err)
})
},[])
return (
<View>
<Header navigate={navigation}/>
<FlatList
data={data}
renderItem={(item) => (
<Text>
{item.name}
</Text>
)}
/>
</View>
)

Related

React Native - Render FlatList using API data

I'm trying to get data from an external API and then render it into a flatlist.
I'm very new to React Native so this may be easy to solve.
I'm trying to use the following data: https://www.nationaltrust.org.uk/search/data/all-places
I want to fetch it from the URL, and render the 'title' and 'imageUrl' fields into a flatlist component.
This is what I have so far:
const placesURL = "https://www.nationaltrust.org.uk/search/data/all-places";
const [isLoading, setLoading] = useState(true);
const [places, setPlaces] = useState([]);
useEffect(() => {
fetch(placesURL)
.then((response) => response.json())
.then((json) => setPlaces(json))
.catch((error) => alert(error))
.finally(setLoading(false));
})
And in the flatlist:
export default function App() {
return (
<View style={styles.container}>
<FlatList
data={places}
renderItem={({ item }) => (
<Text>{item.title}</Text>
)}
keyExtractor={(item) => item.id}
/>
<StatusBar style="auto" />
</View>
);
}
If anyone could tell me what to do I would really appreciate it.
try updating your useEffect hook to this
useEffect(() => {
if(places.length === 0 && isLoading){
fetch(placesURL)
.then((response) => response.json())
.then((json) => setPlaces(json))
.catch((error) => alert(error))
.finally(setLoading(false));
}
}, [places, isLoading])
and
export default function App() {
return (
<View style={styles.container}>
{places.length !== 0 &&
<FlatList
data={places}
renderItem={({ item }) => (
<Text>{item.title}</Text>
)}
keyExtractor={(item) => item.id}
/>
}
<StatusBar style="auto" />
</View>
);
}
This URL https://www.nationaltrust.org.uk/search/data/all-places returns a JSON object not an array of objects. It's required to transform an object into an array of objects to be compatible with FlatList.
import React, { useState, useEffect } from "react";
import { Text, View, StyleSheet, FlatList } from "react-native";
const placesURL = "https://www.nationaltrust.org.uk/search/data/all-places";
export default function App() {
const [isLoading, setLoading] = useState(true);
const [places, setPlaces] = useState([]);
const getPlaces = async () => {
try {
const response = await fetch(placesURL);
const result = await response.json();
const newPlaces = Object.values(result);
setPlaces(newPlaces);
setLoading(false);
} catch (error) {
setLoading(false);
console.log(error);
}
};
useEffect(() => {
getPlaces();
}, []);
if (isLoading) {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text> Searching places.... </Text>
</View>
);
}
return (
<View style={styles.container}>
<FlatList
data={places}
renderItem={({ item }) => <Text>{item.title}</Text>}
keyExtractor={(item) => item.id}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
padding: 20,
},
});
Here is Expo Snack for testing - https://snack.expo.dev/#emmbyiringiro/a98de6
Note: Use Android or iOS emulator, not Web preview.

Why can't I navigate to my Screen using onPress={() => navigation.navigate('SaveProfileImage, { profileImage })}?

I'm developing an app where I have a screen called Add, other called Save, that I navigate between them using onPress={() => navigation.navigate('Save', { image })} inside a button, so I tried to do the same on another screen called profile that should navigate to the screen SaveProfileImage, but when I try this, I get the error TypeError: undefined is not an object (evaluating 'props.route.params') and I can't understand what can be so wrong in my codes:
Here is the Profile codes:
import React, { useState, useEffect } from 'react';
import { StyleSheet, View, Text, Image, FlatList, Button } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import firebase from 'firebase';
require('firebase/firestore');
import { connect } from 'react-redux';
function Profile({ navigation }, props) {
const [userPosts, setUserPosts] = useState([]);
const [user, setUser] = useState(null);
const [hasGalleryPermission, setHasGalleryPermission] = useState(null);
const [image, setImage] = useState(null);
useEffect(() => {
const { currentUser, posts } = props;
if (props.route.params.uid === firebase.auth().currentUser.uid) {
setUser(currentUser)
setUserPosts(posts)
}
else {
firebase.firestore()
.collection('users')
.doc(props.route.params.uid)
.get()
.then((snapshot) => {
if (snapshot.exists) {
setUser(snapshot.data());
}
else {
console.log('does not exist')
}
})
firebase.firestore()
.collection('posts')
.doc(props.route.params.uid)
.collection('userPosts')
.orderBy('creation', 'desc')
.get()
.then((snapshot) => {
let posts = snapshot.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return { id, ...data }
})
setUserPosts(posts)
});
};
(async () => {
const galleryStatus = await ImagePicker.requestMediaLibraryPermissionsAsync();
setHasGalleryPermission(galleryStatus.status === 'granted');
})();
}, [props.route.params.uid, props.following]);
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
if (!result.cancelled) {
setImage(result.uri);
};
};
if (hasGalleryPermission === false) {
return <View />;
};
if (hasGalleryPermission === false) {
return <Text>No access to gallery</Text>;
};
const onLogout = () => {
firebase.auth().signOut();
};
if (user === null) {
return <View />
}
return (
<View style={styles.container}>
{image && <Image source={{ uri: image }}
style={{ flex: 1 }} />}
<Image source={{ uri: props.route.params.profileImage }} />
<View style={styles.containerInfo}>
<Text>{user.name}</Text>
<Text>{user.state}</Text>
<Text>{user.city}</Text>
<Text>{user.email}</Text>
{props.route.params.uid !== firebase.auth().currentUser.uid}
<Button title='Pick Image From Gallery' onPress={() => pickImage()} />
<Button title='Save'
onPress={() => navigation.navigate('SaveProfileImage',
{ profileImage })} />
</View>
<View style={styles.container}>
<FlatList
numColumns={3}
horizontal={false}
data={userPosts}
renderItem={({ item }) => (
<View
style={styles.containerImage}>
<Image
style={styles.image}
source={{ uri: item.downloadURL }}
/>
</View>
)}
/>
</View>
<Button
title='Logout'
onPress={() => onLogout()}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
containerInfo: {
margin: 20
},
containerImage: {
flex: 1 / 3
},
image: {
flex: 1,
aspectRatio: 1 / 1
}
})
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts,
following: store.userState.following
})
export default connect(mapStateToProps, null)(Profile);
The app.js has a navigation container to control these screens:
<NavigationContainer >
<Stack.Navigator initialRouteName='Main'>
<Stack.Screen name='Main' component={MainScreen} />
<Stack.Screen name='Add' component={AddScreen}
navigation={this.props.navigation}/>
<Stack.Screen name='Save' component={SaveScreen}
navigation={this.props.navigation}/>
<Stack.Screen name='SaveProfileImage' component={SaveProfileImageScreen}
navigation={this.props.navigation}/>
<Stack.Screen name='Comment' component={CommentScreen}
navigation={this.props.navigation}/> </Stack.Navigator>
<NavigationContainer >
I can't understand why I can navigate from the AddScreen to the Save Screen, but I can't do the same between the profileScreen and the SaveProfileImage.
The save is this one:
import React, { useState } from 'react'
import { View, TextInput, Image, Button } from 'react-native'
import firebase from 'firebase'
require('firebase/firestore')
require('firebase/firebase-storage')
export default function Save(props) {
const [caption, setCaption] = useState('')
const uploadImage = async () => {
const uri = props.route.params.image;
const childPath =
`post/${firebase.auth().currentUser.uid}/${Math.random().toString(36)}`;
const response = await fetch(uri);
const blob = await response.blob();
const task = firebase
.storage()
.ref()
.child(childPath)
.put(blob);
const taskProgress = snapshot => {
console.log(`transferred: ${snapshot.bytesTransferred}`)
};
const taskCompleted = () => {
task.snapshot.ref.getDownloadURL().then((snapshot) => {
savePostData(snapshot);
})
};
const taskError = snapshot => {
console.log(snapshot)
};
task.on('state_changed', taskProgress, taskError, taskCompleted);
};
const savePostData = (downloadURL) => {
firebase.firestore()
.collection('posts')
.doc(firebase.auth().currentUser.uid)
.collection('userPosts')
.add({
downloadURL,
caption,
likesCount: 0,
estate: '',
city: '',
creation: firebase.firestore.FieldValue.serverTimestamp()
}).then((function () {
props.navigation.popToTop()
}))
};
return (
<View style={{ flex: 1 }}>
<Image source={{ uri: props.route.params.image }} />
<TextInput
placeholder='Write a Caption . . .'
onChangeText={(caption) => setCaption(caption)}
/>
<Button title='Save' onPress={() => uploadImage()} />
</View>
);
};
The SaveProfileScreen is this other one:
import React, { useState } from 'react'
import { View, TextInput, Image, Button } from 'react-native'
import firebase from 'firebase'
require('firebase/firestore')
require('firebase/firebase-storage')
export default function ProfileImage(props) {
const [caption, setCaption] = useState('')
const uploadImage = async () => {
const uri = props.route.params.image;
const childPath =
`profileImage/${firebase.auth().currentUser.uid}/
${Math.random().toString(36)}`;
console.log(childPath);
const response = await fetch(uri);
const blob = await response.blob();
const task = firebase
.storage()
.ref()
.child(childPath)
.put(blob);
const taskProgress = snapshot => {
console.log(`transferred: ${snapshot.bytesTransferred}`)
};
const taskError = snapshot => {
console.log(snapshot)
};
const taskCompleted = () => {
task.snapshot.ref.getDownloadURL().then((snapshot) => {
savePictureData(snapshot);
})
};
task.on('state_changed', taskProgress, taskError, taskCompleted);
};
return (
<View style={{ flex: 1 }}>
<Image source={{ uri: props.route.params.profileImage }} />
<Button title='Save' onPress={() => uploadImage()} />
</View>
);
};

How to fetch json and display img in flatlist (React Native)

I'm working on a small react native app which can fetch json and display gifs in flatlist.
But it is not working and i don't know where is the problem!
export default function App() {
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://api.tenor.com/v1/random?key=1FS646G91JAT&q=meme&limit=50')
.then((response) => response.json())
.then((json) => {
const array_URL = []
for (var i = 0; i < 50; i++) {
array_URL.push(
{
id: i + 1,
url: json.results[i].media[0]["mediumgif"].url
}
)
}
setData(array_URL)
})
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
return (
<View style={{ flex: 1 }}>
{isLoading ? <ActivityIndicator /> : (
<FlatList
data={data}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<Image
source={{ uri: item.url }}
/>
)}
/>
)}
</View>
);
}
Can anyone please tell me where the problem is?
Thanks a lot!
Here is a demo.
snack: https://snack.expo.io/#ashwith00/belligerent-celery
import React, { useState, useEffect } from 'react';
import {
View,
Text,
ScrollView,
useWindowDimensions,
ActivityIndicator,
FlatList,
Image,
} from 'react-native';
export default function App() {
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://api.tenor.com/v1/random?key=1FS646G91JAT&q=meme&limit=10')
.then((response) => response.json())
.then((json) => {
const array_URL = [];
for (var i = 0; i < 10; i++) {
array_URL.push({
id: i + 1,
url: json.results[i].media[0]['mediumgif'].url,
});
}
setData(array_URL);
})
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
console.log(data);
return (
<View style={{ flex: 1 }}>
{isLoading ? (
<ActivityIndicator />
) : (
<FlatList
extraData={data}
data={data}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<Image
resizeMode="cover"
style={{ alignSelf: 'stretch', height: 200 }}
source={{ uri: item.url }}
/>
)}
/>
)}
</View>
);
}

I'm trying to run a search with user input, but my searchInput variable is showing up as undefined?

I'm trying to run a search with user input, but my searchInput variable is showing up as undefined when I run the code. I can't figure out what I'm doing wrong. Thanks for your help!
Here's my code:
import React, { useState } from "react";
import {
TouchableOpacity,
StyleSheet,
View,
Modal,
TextInput
} from "react-native";
import Icon from "react-native-vector-icons/Feather";
import API_KEYS from "../utils/APIKeys";
const SearchScreen = ({ modalVisible, setModalVisible }) => {
const [searchInput, setSearchInput] = useState("");
const [searchResults, setSearchResults] = useState([]);
const searchPlaces = ({ searchInput }) => {
console.log(searchInput);
fetch(
`https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${searchInput}&types=cities&key=${API_KEYS.GOOGLE_MAPS_API_KEY}`
)
.then(res => res.json())
.then(json => {
console.log(json);
});
};
return (
<Modal animationType="fade" transparent={false} visible={modalVisible}>
<TouchableOpacity
style={styles.iconContainer}
onPress={() => setModalVisible(false)}
>
<Icon name="x" size={30} />
</TouchableOpacity>
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Search places…"
placeholderTextColor="#666"
value={searchInput}
onChangeText={newValue => setSearchInput(newValue)}
onEndEditing={searchInput => searchPlaces(searchInput)}
/>
</View>
</Modal>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
height: "100%",
justifyContent: "center",
marginHorizontal: 20
},
input: {
fontFamily: "hermitRegular",
fontSize: 24
},
iconContainer: {
zIndex: 10,
position: "absolute",
top: 60,
right: 25
}
});
export default SearchScreen;
Here's the response. The first line is the content of searchInput and the second is the response from the Google API.
undefined
Object {
"predictions": Array [],
"status": "INVALID_REQUEST",
}
I figured out what was wrong.
This needed updating:
const searchPlaces = ({ searchInput }) => {
console.log(searchInput);
fetch(
`https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${searchInput}&types=cities&key=${API_KEYS.GOOGLE_MAPS_API_KEY}`
)
.then(res => res.json())
.then(json => {
console.log(json);
});
};
To this:
const searchPlaces = () => {
console.log(searchInput);
fetch(
`https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${searchInput}&types=(cities)&key=${API_KEYS.GOOGLE_MAPS_API_KEY}`
)
.then(res => res.json())
.then(json => {
console.log(json);
});
};

How to update state react native hooks from other screen using react navigation hook param?

How to update state react native hooks from other screen using react navigation hook param?
I am trying to update state selectedHotel in screen 1 from screen 2 that provide the data, so i save data from screen 2 in parameter using react navigation hooks params, the data is update but i can't update state selectHotel if data is exist in useEffect screen 1, here the code:
screen 1:
import {useNavigation, useNavigationParam} from 'react-navigation-hooks';
const TransportScreen = () => {
const hotelParam = useNavigationParam('hotel');
const [baseLoading, setBaseLoading] = useState(true);
const [selectedHotel, setSelectedHotel] = useState(
hotelParam ? hotelParam.id : '',
);
const {navigate} = useNavigation();
useEffect(() => {
setTimeout(() => {
setBaseLoading(false);
}, 1000);
if (hotelParam) {
setSelectedHotel(hotelParam.id);
console.log('update selected hotel', selectedHotel);
}
}, []);
const redirectTransportSelectHotel = () => {
console.log('select hotel');
navigate('TransportSelectHotel');
};
const submitTransport = () => {
console.log('getHotelId ', selectedHotel);
};
const renderContent = () => {
console.log('hotelId: ', selectedHotel);
if (!baseLoading) {
return (
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-start',
}}>
<MenuCard
expandRight
onPress={() => redirectTransportSelectHotel()}>
{hotelParam ? hotelParam.name : 'Select Hotel'}
</MenuCard>
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-end',
}}>
<View
style={{
flexDirection: 'row',
marginHorizontal: 40,
marginVertical: 20,
}}>
<Button onPress={() => submitTransport()}>Submit</Button>
</View>
</View>
</View>
);
}
return <LoaderScreen visible={baseLoading} />;
};
};
screen 2:
import {useNavigation, useNavigationParam} from 'react-navigation-hooks';
import {useSelector, useDispatch} from 'react-redux';
import {getHotels} from './actions';
import _ from 'lodash';
const TransportSelectHotelScreen = () => {
const {navigate} = useNavigation();
const dispatch = useDispatch();
const [baseLoading, setBaseLoading] = useState(true);
const {hotel} = useSelector(state => ({
hotel: state.transportHotelReducer,
}));
useEffect(() => {
setTimeout(() => {
setBaseLoading(false);
}, 1000);
loadHotels();
}, [loadHotels]);
const handleRefresh = () => {
console.log('refresh');
loadHotels();
};
const loadHotels = async () => {
dispatch(getHotels());
};
const redirectTransportCallback = hotel => {
console.log('hotel detail', hotel);
navigate('Transport', {hotel: hotel});
};
const renderItem = item => {
return (
<MenuCard
expandRight
onPress={() => redirectTransportCallback(item.item)}>
{item.item.name}
</MenuCard>
);
};
const renderContent = () => {
if (!baseLoading) {
if (!hotel.hotels.baseLoading) {
if (!_.isEmpty(hotel.hotels)) {
return (
<View style={globalStyles.menuContainer}>
<FlatList
data={hotel.hotels}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
refreshing={hotel.isRefreshing}
onRefresh={handleRefresh}
// onEndReached={handleLoadMore}
// onEndReachedThreshold={0.1}
/>
</View>
);
} else {
return (
<View style={globalStyles.wrapperContent}>
<Text>{Lang.no_data}</Text>
</View>
);
}
}
return <LoaderScreen visible={hotel.baseLoading} />;
}
return <LoaderScreen visible={baseLoading} />;
};
};
You could try
useEffect(() => {
setSelectedHotel(hotelParam ? hotelParam.id : '')
}, [hotelParam])