react-native-draggable-flatlist
react-native-draggable-flatlist copied to clipboard
List blinking for fraction of second when rearranging the items.
List blinking for fraction of second when rearranging the items. please find the attached video . this issue is not happening in version3.1.2 and happening in version 4.0.0.
https://user-images.githubusercontent.com/37132763/206464911-c575affb-8acb-408b-9b64-cfa960472a94.MOV
any update on this? @@
The same issue...
Can you reproduce this in a shareable repo? I had the issue and noticed that I was passing a custom useState prop that was changing some style and thus causing a second rerender after the item was dropped.
Tried to create a reproducible sample, but couldn't get it to work in a smaller environment
~I'm seeing this as well. @Svarto This is also noticeable in the sample iOS snack shown in the README.md of this project.~
EDIT: Running the Expo example on my phone showed it does not flicker. I believe this is an issue on my end.
For me, I found that if I was firing a server request within onDragEnd it would cause this flash.
I solved the issue by putting that request inside of a setTimeout.
onDragEnd={({ data }) => {
// Update client state
setTimeout(() => {
// Update server state
}, 0);
}}
replace
keyExtractor={(item, index) => index.toString()}
to
keyExtractor={(item, index) => item.xxxKey}
replace
keyExtractor={(item, index) => index.toString()}tokeyExtractor={(item, index) => item.xxxKey}
^ Not using the index as the keyExtractor fixed this on my end. Which makes sense since you are changing the index of the data on drag end and it needs to rerender and the dragged item will have a different index before its position is changed in the array of items. Use something instead that doesnt change regardless of how your items are rendered.
For me, I found that if I was firing a server request within onDragEnd it would cause this flash.
I solved the issue by putting that request inside of a setTimeout.
onDragEnd={({ data }) => { // Update client state setTimeout(() => { // Update server state }, 0); }}
This was my issue as well. It was resolved by putting a timeout around the server update (network call). Thank you 👏
When update react-native-reanimated to 3.0.1 It seems to have solved this problem
I'm using RealmsDB and it's happening to me. My data is get like this:
const banks = useQuery(AppBank).sorted('order');
const bankAccounts = Array.from(banks);
This are my functions:
const handleDragEnd = useCallback((data: AppBank[]) => {
realm.write(() => {
data.forEach((item, index) => {
const realmItem = realm.objectForPrimaryKey(AppBank, item._id);
if (realmItem) {
realmItem.order = index;
} else {
throw "Item shouldn't be null";
}
});
});
}, []);
const handleRemoveItem = useCallback((appBank: AppBank) => {
realm.write(() => {
const itemToDelete = realm.objectForPrimaryKey(AppBank, appBank._id);
if (itemToDelete) {
realm
.objects(AppBank)
.filtered(`order > ${itemToDelete.order}`)
.forEach(item => {
item.order -= 1;
});
realm.delete(itemToDelete);
}
});
}, []);
Callback or not, the list will flicker for a second if dragging.
A strange thing happens also when I delete an Item. Every item in Realm DB has a unique key, but for some reason the list will render "old" objects and then the new ones. So one of the items got deleted, hence not valid, but my keyExtractor function still sees it and will throw an exception. This is my workaround (but still the flicker persist):
<DraggableFlatList
data={bankAccounts}
keyExtractor={item => (item.isValid() ? item._id.toHexString() : 'DELETE')}
onDragEnd={({ data }) => handleDragEnd(data)}
// Passing also ITEM with handleRemoveItem...
...
I have only one re-render happening in my screen when updating. I made a console.log and it happens only once for every delete of the item. I'm using Reanimated 3.0.1. I tired the "setTimeout" thing but it's not working.
A TOTAL workaround that I'm using right now is like this. It's working and not causing the flicker. But it's not how I wanted to manage my data.
const realm = useRealm();
const [bankAccounts, setBankAccounts] = useState<AppBank[]>([]);
useEffect(() => {
setBankAccounts(Array.from(realm.objects(AppBank).sorted('order')));
}, []);
const handleDragEnd = useCallback((data: AppBank[]) => {
realm.write(() => {
data.forEach((item, index) => {
const realmItem = realm.objectForPrimaryKey(AppBank, item._id);
if (realmItem) {
realmItem.order = index;
} else {
throw "Item shouldn't be null";
}
});
});
// FORCING UPDATE ONLY AFTER ALL (same for the other function)
setBankAccounts(Array.from(realm.objects(AppBank).sorted('order')));
}, []);
...
<DraggableFlatList
data={bankAccounts}
// Key problem still persist and filtering the array is not working :(
keyExtractor={item => (item.isValid() ? item._id.toHexString() : 'DELETE')}
onDragEnd={({ data }) => handleDragEnd(data)}
// Passing also ITEM with handleRemoveItem...
@rifad4u this still occurs, why did you close this issue?
Hi I am also facing same issue i am not making any network call I am simply updating setting state (data) .
Hi I am also facing same issue i am not making any network call I am simply updating setting state (data) .
Try this keyExtractor={(item, index) => JSON.stringify(item)}
I have item.id as unique key I am using that as key extractor. @rifad4u Can you please explain me what JSON.stringify(item) will do different?
I have item.id as unique key I am using that as key extractor. @rifad4u Can you please explain me what JSON.stringify(item) will do different?
Can u show your keyExtractor line
@rifad4u keyExtractor={(item) => item.meterId} Where meterId is unique for all items. This is of type string also.
Fixed this on my local project today. It can happen for multiple reasons:
- Using index as key
- Personally, I never encountered this issue despite using the index as the key, however it was reported by others in the thread so no harm in refactoring if possible.
- Just change it to the item.id or some other value from the object that is not dependent on the index
- Performing too many actions in the
onDragEnd- This turned out to be my issue.
- You basically just need to refactor your
onDragEndfrom something like this:
to something like this:onDragEnd={({data}) => { setList(data); // a bunch of other actions }}import { InteractionManager } from 'react-native'; .... onDragEnd={({data}) => { setList(data); InteractionManager.runAfterInteractions(() => { // a bunch of other actions }); }}InteractionManagerwaits until all animations are complete before running basically
Hello, I have the same issue happening, but only in a web browser on desktop. On mobile it works fine. I am not performing any other action in the onDragEnd just updating my state and the keyExtractor has a unique id for each item in my list.
Any suggestions how to fix this?
@Sbstnklt you would need to provide more information like code. A bit hard to help without context
I have nearly the exact same implementation as described in https://github.com/computerjazz/react-native-draggable-flatlist/issues/434#issuecomment-1464981428. The only difference is that my array is an embedded realm array/list. I've tried every solution offering in this thread and nothing works. I suspect the problem is that inside onDragEnd() I'm updating realm via realm.write() by setting the 'order' property of each item in a loop (which takes time). In the mean time the component is rendering on the drop using the old (pre-drag) state.
const reorderActions = (data: Realm.List<ChecklistAction> | Omit<ChecklistAction, keyof Realm.Object>[]) => {
if (checklistTemplate) {
realm.write(() => {
for (let i = 0; i < data.length; i++) {
data[i].ordinal = i;
};
});
}
};
Having long running sync work inside onDragEnd() is really the problem - but not sure anything can really be done about it. The UI can't wait of the UX will be awful (list item freezes before it drops). The realm writes clearly take too long.
It seems the only solution is to copy the list item data out of the realm object and use a simple (low overhead) setState() and then later update realm by copying from state into a realm.write(). Can it get any uglier?
@ajp8164
Having long running sync work inside onDragEnd() is really the problem - but not sure anything can really be done about it. The UI can't wait of the UX will be awful (list item freezes before it drops). The realm writes clearly take too long.
https://github.com/computerjazz/react-native-draggable-flatlist/issues/434#issuecomment-1490929663 Use InteractionManager.runAfterInteractions
Thanks for the response - I tried this but I had the drag list items bound to the realm array directly so using InteractionManager doesn't fix it. I had to move the items to a state variable and put the realm write in a useEffect.
I have the same issue when setting data with TanstackQuery. If I use a simple useState then I am good but then I don't get the benefit of using TanstackQuery's cache and update managements.
I have the same issue on web.
the following is my code.
simply update the local data at onDragEnd callback and use unique strings as the keys
import { useCallback, useState } from 'react';
import { Pressable, Text } from 'react-native';
import DraggableFlatList, {
RenderItem,
ScaleDecorator,
DragEndParams,
} from 'react-native-draggable-flatlist';
export default () => {
const [ids, setIds] = useState<string[]>(['A', 'B', 'C', 'D']);
const handleDragEnd = useCallback(({ data }: DragEndParams<string>) => {
setIds(data);
}, []);
const renderItem = useCallback<RenderItem<string>>(({ item, drag, isActive }) => {
return (
<ScaleDecorator activeScale={1.05}>
<Pressable onLongPress={drag} disabled={isActive}>
<Text>{item}</Text>
</Pressable>
</ScaleDecorator>
);
}, []);
return (
<DraggableFlatList
data={ids}
keyExtractor={item => item}
renderItem={renderItem}
onDragEnd={handleDragEnd}
/>
);
};
library version: "react": "18.2.0", "react-native": "0.72.6", "react-native-draggable-flatlist": "^4.0.1", "react-native-gesture-handler": "~2.12.0", "react-native-reanimated": "~3.3.0", "react-native-web": "~0.19.6",