Home > Back-end >  Mapping array in state error, ' Warning: Encountered two children with the same key '
Mapping array in state error, ' Warning: Encountered two children with the same key '

Time:07-26

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()
 
    }, [])


CodePudding user response:

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.

CodePudding user response:

<Pressable key={index} onPress={//do something}> <-- key passed here is redundant, as long as you you use FlatList, keyExtractor is more than enough and will do the job

Using index as a key is not recommended

You 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

  • Related