I'm attempting to build an app that imports contacts using react-native-contacts and displays that in a list.
My issue is that I'm getting an unhandled promise rejection only on the first render. Here's my code for the component, with the error happening when I log to console in the effect:
function ContactsList() {
const [contacts, setContacts] = useState({});
// I think this function is fine, but including it for context because it might not be
function sortData(data) {
const dataSorted = {
'A': [],
'B': [],
'C': [],
\\etc...
};
data.sort((a, b) => (a['name'] > b['name']) ? 1 : -1);
for (let x in data) {
const first = data[x]['name'][0];
var letters = /^[A-Za-z]+$/;
if (first.match(letters)) {
dataSorted[first].push(data[x]);
} else {
dataSorted['#'].push(data[x]);
}
}
setContacts(dataSorted);
}
useEffect(() => {
(async () => {
const { status } = await Contacts.requestPermissionsAsync();
console.log('permission ' + status);
if (status === 'granted') {
const { data } = await Contacts.getContactsAsync().catch((error) => {console.error(error)});
if (data.length > 0) {
sortData(await data);
console.log(contacts['A'][0]); // THIS IS THE LINE THAT THROWS AN ERROR
}
} else {
Alert.alert(
"Contacts Permission",
"Sorry, we can't load your contacts because permission was not granted.",
[{ text: "OK", onPress: () => console.log("OK Pressed") }],
{ cancelable: false }
);
}
})();
}, []);
return (
<View>
<Text> These are contacts: </Text>
{contacts['A'] && // I'm using && to stop it from rendering the list before the data gotten
<View/> // I'll add my section list here
}
</View>
);
}
When I reload my app, I get this error:
[Unhandled promise rejection: TypeError: undefined is not an object (evaluating 'contacts['A'][0]')]
I thought the "if (data.length > 0)" part would stop it from trying if data is empty as shown in this documentation: https://docs.expo.io/versions/latest/sdk/contacts/
If I do a hot reload, it works fine and I don't get any warnings. The unhandled promise rejection only happens on the first render.
I've read up on more examples and gone through similar questions, but I still can't find what I'm missing. I'm new to react native, so any help would be appreciated. I could certainly be misunderstanding something that should be obvious. Thanks!
The issue is that when you do
setContacts(dataSorted)
that won't be immediately available in the state - it works like setState
did in that
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
https://reactjs.org/docs/react-component.html#setstate
What you could do is just return dataSorted
and then log what the state will be:
function sortData(data) {
// ...
setContacts(dataSorted);
return dataSorted;
}
// ......
if (data.length > 0) {
const nextContacts = sortData(await data);
console.log(nextContacts['A'][0]);
}