My react native app populates a list of a users photo albums, displaying some of the images and key information about the album.
Initially, I iterated through each photo album, pushing the necessary data to a new array. Once completed I set this array to state and displayed it via FlatList. This worked fine with no errors but the problem with this is that there is quite a long loading time as you have to wait until the information is retrieved for ALL albums before ANYTHING is displayed to the user.
So instead, I have opted to essentially push each new object containing the information about an album to an array in state. That way, an album will display as soon as it is retrieved rather than waiting for all other albums to be loaded. The code looks like this (simplifying code for illustrative purposes):
const [albums, setAlbums] = useState([])
const getAllAlbums = async () => {
//iterate over each album and call api to retrieve necessary information
setAlbums(prevState => [...prevState, {
albumName: 'xyz',
numberOfPhotos: 'xyz',
previewImage:'xyz',
...etc
}]
}
I then return a FlatList which looks like this (leaving out styles for simplicity):
<FlatList
data={albums}
key={2}
renderItem={({item, index})=>{
return (
<Pressable key={index} onPress={//do something}>
<View>
<Image source = {{uri: item.preview}}/>
<Text>{item.title}: {item.assetCount} imgs</Text>
</View>
</Pressable>
)
}}
keyExtractor={(item)=>item.id}
/>
The code works, the albums display. But for some reason I keep getting this message:
Warning: Encountered two children with the same key, BBD020CF-7E5C-412D-BA1A-D9C12ACAD91A:ACF588A8-A07E-4DD4-ADC2-E12BB0A1BCF3
. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
There seems to be a duplication in what is displayed to users. Instead of retrieving a list of albums once (i.e. album 1, album 2, album 3, album 4), the output seems to repeat/duplicate several times (i.e. album 1, album 2, album 3, album 4, album 1, album 2, album 3, album 4, album 1, album 2, album 3, album 4).
That is obviously the cause of the problem, but I have no idea why this would be happening. Advice appreciated. Thanks.
EDIT
Full getAllAlbums function is here:
useEffect(async()=>{
const getAllAlbums = async () => {
//retrieve all album
const getAlbums = await MediaLibrary.getAlbumsAsync({includeSmartAlbums: true})
//filter out empty albums
const albumsWithPhotos = getAlbums.filter((item)=>{
return item.assetCount>0
})
for(let i=0; i<albumsWithPhotos.length; i++){
//get the most recent photo in the album to use as cover
const mostRecentPhoto = await MediaLibrary.getAssetsAsync({album: albumsWithPhotos[i].id, mediaType: 'photo', first: 1})
//prevent crashes
if (typeof(mostRecentPhoto.assets[0]) == 'undefined'){
continue;
}
//get the local uri needed by image component
const updatedPhoto = await MediaLibrary.getAssetInfoAsync(mostRecentPhoto.assets[0].id)
if(updatedPhoto){
const compressedImage = await ImageManipulator.manipulateAsync(updatedPhoto.localUri, [], { compress: 0.1 });
//add album to state array
setAlbums(prevState => [...prevState, {
title: albumsWithPhotos[i].title,
assetCount: albumsWithPhotos[i].assetCount,
id: albumsWithPhotos[i].id,
preview: compressedImage.uri
}])
}
}
}
const result = await getAllAlbums()
}, [])
2
Answers
You can use your url as key.
The reason to that behavior is that it iterates and re-renders the last part of the album that you added not all the album. So starts counting from 0 again resulting in duplicated keys.
<Pressable key={index} onPress={//do something}>
<– key passed here is redundant, as long as you you useFlatList
,keyExtractor
is more than enough and will do the job++
Using
index
as akey
is not recommendedYou should help your
FlatList
render items that only changed per renders by passing a real key that distinguishes each item in your list,So try to use
id
, or any other field in your list that you’re sure is unique per list-item