React Native FlashList "flickers" with state change

993 views Asked by At

I am currently using FlashList to show a list of results retrieved from the device's native contact list. The idea is when you select a contact(s) there is a checkbox that appears to each contact indicating it's select. However after this implementation, I have noticed that FlashList component flickers with each selection. I quickly switched to the React Native FlatList with no problems, so it clearly has to do with FlashList specifically.

I have verified that I have defined a unique key using the keyExtractor prop, and I have set the extraData prop using the selected data. Based on what I read from the documentation, this should have solved my issue but it did not. Any help would be appreciated.

My Component View

<View style={{ display: 'flex', flex: 1, width: '100%', flexDirection: 'column' }}>
                <View style={{ flex: 1, width: '100%' }}>
                    <FlashList
                        data={contacts}
                        keyExtractor={(item, index) => `${item.id}-${index}`}
                        ref={contactList}
                        extraData={selectedContacts}
                        renderItem={renderItem}
                        estimatedItemSize={100}
                    />
                </View>
            </View>

Component Code

const contactList = useRef()
const [selectedContacts, setSelectedContacts] = useState<string[]>([]);

const renderItem = ({ item }) => (
        <ContactListResult item={item}
                           onPress={toggleSelection}
                           selectable={true}
                           selected={selectedContacts.indexOf(item.id) >= 0}
        />
    );

const toggleSelection = (item : ConnectionListItem) => {
        let currentList = [...selectedContacts];
        if (currentList.includes(item.id)) {
            currentList.splice(currentList.indexOf(item.id), 1)
        } else {
            currentList.push(item.id)
        }
        setSelectedContacts(currentList)
    }
1

There are 1 answers

1
T_DaMER On

You have a re-render here: selectedContacts passed as extraData -> setSelectedContacts called from the rendered item -> selectedContacts updates and triggers re-rendering of FlashList


Possible solution: don't pass selectedContacts as extraData, simply indicate selection by checkbox in UI + call setSselectedContacts. Process selectedContacts later in a callback function

Rewrite this function to avoid updating the callback too often:

const toggleSelection = useCallback((item : ConnectionListItem) => {
   setSelectedContacts((currentList) => currentList.push(item.id))
}, [])

Rewrite your component to simply display UI logic:

        <ContactListResult item={item}
                           onPress={toggleSelection}
                           selectable={true}
    // don't pass `selected`, make `const displayCheckbox = useState(false)` 
    // inside this component and trigger checkbox appearing 
    // `if (displayCheckbox !== true) return null` when the user pressed ListItem
        />

Later in your callback function (where you use selectedContacts) simply call [...Set(selectedContacts] to filter duplicates of item.id