I'm just training how to consume apis in react-native with useEffects and in console.log () returns the results, but in the view it doesn't show, I think it's the keyExtractor or not ...
const api = 'https://randomuser.me/api/?&results=2';
const Detalhes = () => {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
setIsLoading(true);
fetch(api)
.then((response) => response.json())
.then((results) => {
setData(results);
setIsLoading(false);
console.log(results);
})
.catch((err) => {
setIsLoading(false);
setError(err);
});
}, []);
if (isLoading) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator size="large" color="#5500dc" />
</View>
);
}
if (error) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text style={{fontSize: 18}}>
Error...
</Text>
</View>
);
}
and this is the code with FlatList that should be returned in View and as said, I can see the result in console.log (react native debug)
<SafeAreaView>
<FlatList
data={data}
keyExtractor={item => item.first}
renderItem={({item}) => (
<View style={styles.metaInfo}>
<Text style={styles.text}>
{`${item.name.first} ${item.name.last}`}
</Text>
</View>
)}
/>
</SafeAreaView>
Try to see again console.log(results). Api response is an object { results, info }, not an array. The results property of results is array.
Replace setData(results); by setData(results.results); to resolve your problem.
Sorry for my bad English.
You are giving name in your keyExtractor which could be same repeat so It won't work. Try changing keyExtractor like below to provide unique index for each item
keyExtractor={(item,index)=>index.toString()}
Related
Hello I'm facing with render error in my movie app during printing results for movie searching. Im working in React-Native 0.70.5. Here is some code for this activity
`
import React,{useState,useEffect} from 'react';
import axios from 'axios';
import { View, StyleSheet, Text, TextInput,ScrollView } from "react-native";
const Search = () => {
const apiurl="https://api.themoviedb.org/3/search/movie?api_key=XXX"
const [state,setState] = useState({
s: "Enter a movie",
results:[]
});
const search = () => {
axios(apiurl + "&query="+ state.s).then(({ data }) => {
let results = data.search;
console.log(data);
setState(prevState => {
return{...prevState, results: results }
})
})
}
return (
<View>
<TextInput
onChangeText={text => setState(prevState => {
return {...prevState, s:text}
})}
onSubmitEditing={search}
value={state.s}
/>
<ScrollView>
{state.results.map(result =>(
<View key={result.id}>
<Text>{result.title}</Text>
</View>
))}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
center: {
flex: 1,
justifyContent: "center",
alignItems: "center",
textAlign: "center",
},
});
export default Search;
`
How to change the construction of this function to print movie titles corectly ?
This is just an example and not the best way to do it.
You need a success flag, something like this:
const [success,setSuccess] = useState(false);
const search = () => {
axios(apiurl + "&query="+ state.s).then(({ data }) => {
let results = data.results;
console.log(data);
setState(prevState => {
return{...prevState, results: results }
})
setSuccess(true);
})
}
<ScrollView>
{success && state.results.map(result =>(
<View key={result.id}>
<Text>{result.title}</Text>
</View>
))}
</ScrollView>
I am not sure but you can try this below ScrollView code...
<ScrollView>
{state.results.length>0 && (state.results.map((result) =>(
<View key={result.id}>
<Text>{result.title}</Text>
</View>
)))}
</ScrollView>
use optional chaining. Might be sometimes you don't get result.
<ScrollView>
{state?.results?.map(result =>(
<View key={result?.id}>
<Text>{result?.title}</Text>
</View>
))}
</ScrollView>
Let me know is it helpful or not
I have a problem with my useEffect, I think it's due to the asynchronous functions inside but I don't know how to fix the problem. Let me explain: in my useEffect, I have a function that is used to retrieve user data thanks to the AsyncStorage and I want that in a weather API request I can enter the user's city as a parameter so it works on the spot but when I reload the application it gives me an error message like : currentUserData.user.city is undefined
Here is the code :
const [loading, setLoading] = useState(false);
const [currentData, setCurrentData] = useState({});
const [currentUserData, setCurrentUserData] = useState({});
const navigation = useNavigation();
useEffect(() => {
setLoading(true);
const getUserData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('user');
setCurrentUserData(JSON.parse(jsonValue));
const response = await axios.get(
`https://api.weatherapi.com/v1/current.json?key=${APIKEY}&q=${currentUserData.user.city}&aqi=no&lang=fr`,
);
setCurrentData(response.data.current.condition);
setLoading(false);
} catch (e) {
setLoading(false);
alert(e);
}
};
getUserData();
}, []);
if (loading) {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text
style={{
fontSize: 80,
color: 'red',
}}>
Loading...
</Text>
</View>
);
} else {
return (
<View style={styles.container}>
<View style={styles.content}>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 50,
}}>
<View
style={{flexDirection: 'column', marginLeft: 20, marginTop: 20}}>
<Image
style={{width: 50, height: 50}}
source={{
uri: `https:${
Object.keys(currentUserData).length > 0
? currentData.icon
: ''
}`,
}}
/>
</View>
<Text style={styles.title}>
Bienvenue, {'\n'}{' '}
<Text style={{fontWeight: 'bold'}}>
{Object.keys(currentUserData).length > 0
? currentUserData.user.first_name
: ''}
</Text>
</Text>
<TouchableWithoutFeedback
onPress={() => navigation.navigate('UserScreen')}>
<FontAwesomeIcon
style={{marginRight: 20, marginTop: 20}}
size={35}
icon={faUser}
/>
</TouchableWithoutFeedback>
</View>
</View>
</View>
);
}
}
Please use ? operator to access the currentUserData.user. e.g.currentUserData?.user?.city.
Because initial state is {}, so currentUserData.user is undefined and your code tried to get value from undefined.
And use in your API URL.
First please write a console. And try to use the following code.
`https://api.weatherapi.com/v1/current.json?key=${APIKEY}&q=${currentUserData? currentUserData?.user?.city || '' : ''}&aqi=no&lang=fr`
Your mistake is that you used the state variable as soon as calling setCurrentUserData. We can't use it in this way. Updated hook is following:
useEffect(() => {
setLoading(true);
const getUserData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('user');
const parsedUserData = JSON.parse(jsonValue) || {};
setCurrentUserData(parsedUserData);
const response = await axios.get(
`https://api.weatherapi.com/v1/current.json?key=${APIKEY}&q=${parsedUserData?.user?.city}&aqi=no&lang=fr`,
);
setCurrentData(response.data.current.condition);
setLoading(false);
} catch (e) {
setLoading(false);
alert(e);
}
};
getUserData();
}, []);
I think you might misuse of React Hook, setCurrentUserData update the state asynchronously, immediately use currentUserData.user.city is completely wrong, thus you either use option below:
Use data returned from AsyncStorage
const jsonValue = await AsyncStorage.getItem('user');
const userData = JSON.parse(jsonValue)
setCurrentUserData(userData);
const response = await axios.get(
`https://api.weatherapi.com/v1/current.json?key=${APIKEY}&q=${currentUserData.user.city}&aqi=no&lang=fr`,
);
Remove logic all below setCurrentUserData(JSON.parse(jsonValue));, and move them into another useEffect with currentUserData as dependency
useEffect(() => {
if(currentUserData) {
// make api request here
const response = await axios.get(
`https://api.weatherapi.com/v1/current.json?key=${APIKEY}&q=${currentUserData.user.city}&aqi=no&lang=fr`,
);
// some logic here...
}
}, [currentUserData])
I am fetching an array of two objects. there are "title" and "iqtiboslar" array inside objects. and show it in SectionList but it is giving an error: "can not read properties of undefined(reading 'length')". Here is my code. Any ideas will be highly appreciated
const Item = ({iqtiboslar}) => (
<View>
<Text>{iqtiboslar}</Text>
</View>
);
const HomeScreen = ({navigation}) => { const [quote, setQuote] = useState();
useEffect(() => {
fetchQuotes();
return () => {
setQuote();
};
}, []);
const fetchQuotes = async () => {
try {
const quoteCollection = await firestore().collection('iqtiboslar').get(); // get(:field) to get specific doc
quoteCollection._docs.map(doc => setQuote(doc.data().items));
// quoteCollection._docs.map(doc => console.log(doc));
} catch (error) {
console.log(error);
}
};
return (
<View style={styles.container}>
{quote ? (
<SectionList
sections={quote}
keyExtractor={(item, index) => item + index}
renderItem={({item}) => <Item title={item.title} />}
renderSectionHeader={({section}) => <Text>{section.title}</Text>}
/>
) : (
<ActivityIndicator />
)} </View>
);
};
export default HomeScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
color: 'red',
},
});
Try this code
<SectionList
sections={quote}
keyExtractor={(item, index) => item + index}
renderItem={({item}) => <Text>{item}</Text>}
renderSectionHeader={({section: {title}}) => (
<Text style={styles.text}>{title}</Text>
)}
I have an API get request here, being called on mount in my useEffect hook and then set
const [multilineAccountInfo, setMultilineAccountInfo] = useState([]);
useEffect(() => {
getMultilineAccountInfo();
return () => {
console.log('cleanup home/balance');
};
}, []);
const getMultilineAccountInfo = () => {
axios
.get('Account/MultiLineAllAccountsInfo')
.then(response => {
let parse_response = JSON.parse(response.data);
let stringified_data = JSON.stringify(parse_response);
setMultilineAccountInfo(stringified_data);
})
.catch(error => {
console.log('getMultilineAccountInfo error', error);
});
};
The problem that I'm facing is that in my View here, the app crashes and says "undefined is not a function (near '...multilineAccountInfo.map...')"
const MultilineDetailHome = multilineAccountInfo.map((item, index) => (
<View key={index}>
<View style={styles.multilineCards}>
<View
style={{
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
}}>
<View>
<Text style={styles.multilineNameText}>
{item.FirstName + ' ' + item.LastName}
</Text>
<Text style={styles.multilinePhoneText}>
{formatPhoneNumber(item.Pnum)}
</Text>
</View>
<View style={{marginLeft: hp('3%')}}>
<View
style={{
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text style={styles.multilineBalanceText}>
${item.Balance.toFixed(2)}
</Text>
</View>
<Text style={styles.multilineTypeText}>{item.AccountType}</Text>
</View>
</View>
</View>
</View>
));
I am pretty sure this is happening because of the async nature of setState() and the data not being returned in time before the .map() method is invoked.
How would I go about waiting for the API call to finish before the .map() and finally rendering the View in the UI?
Thanks for any help!
If you have problem while loading data, you can use this:
multilineAccountInfo.length > 0 ? YOUR COMPONENT : <h1>LOADING</h1>
another way can be using another state like:
[isLoading, setLoading] = useState(true);
and then setting it after your async method.
.then(response => {
let parse_response = JSON.parse(response.data);
let stringified_data = JSON.stringify(parse_response);
setMultilineAccountInfo(stringified_data);
setLoading(false);
})
now You can use it in your component rendering section:
isLoading? Text("Loading"):YOUR COMPONENT
in my app on my homescreen I am rendering a ListList with my items. Now I wanted to add a little info text above it. But it only shows my text and skips(?) my FlatList. Could anyone help me and explain me this behaviour?
If I have my text component in my file it only shows the text. If I only use FlatList it shows correctly my list with my data. But if I try to combine both, it only shows one component. Same thing when I use only FlatList and wrap it inside of View then I get only a white blank screen.
const JobsScreen = (props) => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(false);
const [isRefreshing, setIsRefreshing] = useState(false);
const allJobs = useSelector((state) => state.job.availableJobs);
const loadJobs = useCallback(async () => {
setIsRefreshing(true);
try {
await dispatch(jobActions.fetchAllJobs());
} catch (err) {}
setIsRefreshing(false);
}, [dispatch]);
useEffect(() => {
setIsLoading(true);
loadJobs().then(() => {
setIsLoading(false);
});
}, [dispatch, loadJobs]);
useEffect(() => {
const willFocusSub = props.navigation.addListener("willFocus", loadJobs);
return () => {
willFocusSub.remove();
};
}, [dispatch, loadJobs]);
if (isLoading) {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#2f3640",
}}
>
<ActivityIndicator size="large" color="#fff" animating />
</View>
);
}
return (
<View>
<FlatList
data={allJobs}
onRefresh={loadJobs}
refreshing={isRefreshing}
keyExtractor={(item) => item.id}
style={{ flex: 1, backgroundColor: "#1a191e" }}
renderItem={(itemData) => (
<JobItem
description={itemData.item.description}
titel={itemData.item.titel}
fname={itemData.item.fname}
cover={itemData.item.cover}
genre={itemData.item.genre}
year={itemData.item.year}
id={itemData.item.id}
// friend={itemData.item.friend}
/>
)}
/>
</View>
);
};
Got it by myself.
<View style={{ height: "100%" }}>
solved it.
Try this.
<View style={{ flex: 1 }}>