Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expo SDK 52 upgrade breaks animations #561

Open
kristof-kovacs opened this issue Nov 18, 2024 · 23 comments
Open

Expo SDK 52 upgrade breaks animations #561

kristof-kovacs opened this issue Nov 18, 2024 · 23 comments

Comments

@kristof-kovacs
Copy link

Describe the bug
After an upgrade to Expo SDK version 52, and all relevant dependencies, a previously working horizontal draggable flatlist exhibits two incorrect behaviors:

  1. Drag and drop rearranging the values of the list works, but upon drop, the entries flash back to their original order for a second before settling in the final, correct positions. This make the rearranging feel and look extremely jittery.
  2. With the drag and drop, the item we are dragging sometimes does not resize back down to its normal size after being dropped, leading to an oversized item in the lift. Screenshot of this is attached.

Full code of the component is attached below.

IMG_3A7A57DB5DA1-1

Platform & Dependencies
Please list any applicable dependencies in addition to those below (react-navigation etc).

  • react-native-draggable-flatlist version: ^4.0.1
  • Platform: ios
  • React Native or Expo version: expo 52.0.4
  • Reanimated version: 3.16.1
  • React Native Gesture Handler version: ~2.20.2
  • React Native version: 0.76.1

Previous Platform & Dependencies where this code worked

  • react-native-draggable-flatlist version: ^4.0.1
  • Platform: ios
  • React Native or Expo version: expo 51.0.6
  • Reanimated version: ~3.10.1
  • React Native Gesture Handler version: ^2.16.1
  • React Native version: 0.74.1
    const Favorites = ({ favoriteBooks, isLoading, navigation, onReorder, isCurrentUser, userDetails }) => {
    const { theme } = useContext(ThemeContext);
    const styles = getDynamicStyles(theme);
    const [isEditMode, setIsEditMode] = useState(false);
    const [data, setData] = useState(favoriteBooks);

    useEffect(() => {
        setData(favoriteBooks);
    }, [favoriteBooks]);

    const LibraryButton = () => (
        <TouchableOpacity
            onPress={() => navigation.navigate('Library', {
                userDetails: userDetails
            })}
            style={[
                styles.libraryButton,
                { opacity: isLoading ? 0.5 : 1 }
            ]}
            disabled={isLoading}
        >
            <Ionicons
                name="library-outline"
                size={15}
                color={theme.distinctAccentColor}
                style={{ marginRight: 5 }}
            />
            <Text style={[styles.libraryButtonText, { color: theme.distinctAccentColor }]}>
                {isLoading ? 'Loading...' : 'View Library'}
            </Text>
        </TouchableOpacity>
    );

    const toggleEditMode = async () => {
        if (isCurrentUser) {
            if (isEditMode) {
                // Exiting edit mode, save the new order
                const newOrder = data.map(item => item.id);
                try {
                    const result = await BookService.reorderFavorites(newOrder);
                    if (result) {
                        onReorder(newOrder);
                        setIsEditMode(false);
                    } else {
                        Alert.alert("Error", "Failed to save the new order. Please try again.");
                    }
                } catch (error) {
                    console.error('Error saving favorites order:', error);
                    Alert.alert("Error", "An error occurred while saving. Please try again.");
                }
            } else {
                // Entering edit mode
                setIsEditMode(true);
            }
        }
    };

    useEffect(() => {
        console.log("Setting navigation options. isEditMode:", isEditMode);
        navigation.setOptions({
            swipeEnabled: !isEditMode,
        });
    }, [isEditMode, navigation]);

    const renderPlaceholderCard = () => {
        return (
            <View style={styles.placeholderCard}>
                <Ionicons name="sparkles-outline" size={24} color={theme.subtleAccentColor} />
                <Text style={styles.placeholderText}>Future Gem</Text>
            </View>
        );
    };

    const handleDragStart = useCallback(() => {
        console.log("Drag started");
    }, []);

    const handleDragEnd = useCallback(({ data: newData, from, to }) => {
        console.log("Drag ended. From:", from, "To:", to);
        console.log("New data order:", newData.map(item => item.id));
        setData(newData);
        onReorder(newData.map(item => item.id));
    }, [onReorder]);



    const renderBookItem = useCallback(({ item, drag, isActive }) => {
        if (item === 'placeholder') {
            return renderPlaceholderCard();
        }

        if (isLoading) {
            return (
                <View style={styles.bookItem}>
                    <ShimmerPlaceholder
                        width={styles.thumbnail.width}
                        height={styles.thumbnail.height}
                        style={styles.thumbnail}
                    />
                    <View style={[styles.bookTitle, { height: 16 }]} />
                    <View style={[styles.bookAuthor, { height: 12 }]} />
                </View>
            );
        }

        const book = item.book;
        const navigateToBookDetails = () => {
            if (!isEditMode) {
                navigation.navigate('BookDetails', { book });
            }
        };

        return (
            <TouchableOpacity
                onPress={navigateToBookDetails}
                onLongPress={() => {
                    console.log("Long press detected on book:", book.title, "isEditMode:", isEditMode);
                    if (isEditMode) {
                        console.log("Initiating drag for book:", book.title);
                        drag();
                    }
                }}
                style={[
                    styles.bookItem,
                    isActive && styles.activeBookItem,
                ]}
            >
                <Image source={{ uri: book.cover_url }} style={styles.thumbnail} />
                <Text allowFontScaling={false} style={styles.bookTitle}>{truncate(book.title, 15)}</Text>
                <Text allowFontScaling={false} style={styles.bookAuthor}>{truncate(book.author, 15)}</Text>
            </TouchableOpacity>
        );
    }, [isEditMode, isLoading, navigation, styles, theme]);

    const favoritesList = isLoading
        ? [1, 2, 3, 4, 5]
        : [...data];

    while (favoritesList.length < 5) {
        favoritesList.push('placeholder');
    }

    return (
        <GestureHandlerRootView style={{ flex: 1 }}>
            <View style={styles.favoritesContainer}>
                <View style={styles.favoritesHeaderContainer}>
                    <Text allowFontScaling={false} style={styles.favoritesHeader}>Favorites</Text>
                    {!isCurrentUser && <LibraryButton />}
                    {isCurrentUser && (
                        <TouchableOpacity onPress={toggleEditMode} style={styles.editButton}>
                            <Ionicons
                                name={isEditMode ? "save-outline" : "pencil-outline"}
                                size={20}
                                color={theme.mainTextColor}
                            />
                        </TouchableOpacity>
                    )}
                </View>
                <DraggableFlatList
                    data={favoritesList}
                    renderItem={renderBookItem}
                    keyExtractor={(item, index) => {
                        if (isLoading) return `loading-${index}`;
                        if (item === 'placeholder') return `placeholder-${index}`;
                        return item.id ? item.id.toString() : `item-${index}`;
                    }}
                    horizontal
                    onDragStart={handleDragStart}
                    onDragEnd={handleDragEnd}
                    showsHorizontalScrollIndicator={false}
                    style={styles.favoritesFlatList}
                    contentContainerStyle={styles.favoritesContent}
                    ItemSeparatorComponent={() => <View style={styles.favoritesItemSeparator} />}
                    activationDistance={0}
                    dragItemOverflow={true}
                    dragHitSlop={{ top: 10, bottom: 10, left: 20, right: 20 }}
                    autoscrollThreshold={20}
                    autoscrollSpeed={100}
                />
                <View style={styles.swipeGuideContainer}>
                    <Icon name="arrow-back" size={15} color={theme.subtleAccentColor} />
                    <Icon name="arrow-forward" size={15} color={theme.subtleAccentColor} />
                </View>
            </View>
        </GestureHandlerRootView>
    );
};`
@hodirt
Copy link

hodirt commented Nov 19, 2024

Same here. After upgrading to Expo SDK 52 and rn reanimated to 3.16.1 (as required per Expo), experiencing the identical issue. It's reproducible in a fresh new barebones project.
Also, react native's LayoutAnimation seems to have no effect also after the upgrade (I'm rearranging by list upon a button click)

@shorkyyy
Copy link

This mainly because of new arch, you can set newArchEnabled to false so the animation works normally

@kristof-kovacs
Copy link
Author

Unfortunately, that does not solve it @shorkyyy. It was the first thing I tried when I ran into the issue and I just retried it again. I ensured that I ran a clean build by resetting everything, and both issues I described above still persist.

@CRH-Champ
Copy link

Same problem here.

"react-native-draggable-flatlist": "^4.0.1",
"react-native": "^0.76.1",
"react-native-reanimated": "^3.6.0",

@Unuuuuu
Copy link

Unuuuuu commented Nov 22, 2024

Same problem here.

"react-native-draggable-flatlist": "^4.0.1",
"react-native": "0.76.2",
"react-native-reanimated": "~3.16.1",

0.5x

iPhone.16.Pro.Video.Speed.Change.Nov.21.mp4

@kristof-kovacs
Copy link
Author

+1 for the video @Unuuuuu, this is exactly what I am seeing as well

@fonodi
Copy link

fonodi commented Nov 24, 2024

Also I'm seeing a lot of console errors during the dragging.

(NOBRIDGE) WARN  [Reanimated] Tried to modify key `current` of an object which has been already passed to a worklet. See
https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#tried-to-modify-key-of-an-object-which-has-been-converted-to-a-shareable
for more details.

@kristof-kovacs
Copy link
Author

Yes @fonodi I am seeing the same. You can silence these warnings for the meantime, but I bet they are somehow involved in this behavior

@shorkyyy
Copy link

Unfortunately, that does not solve it @shorkyyy. It was the first thing I tried when I ran into the issue and I just retried it again. I ensured that I ran a clean build by resetting everything, and both issues I described above still persist.

Haven't tested on sdk52 yet but I have tested it on sdk51 and when I enable newarch, the same issue appears and I had no clue what's wrong with it, so I cloned my project and set newarch = false and the issue disappears

@BenGroot
Copy link

BenGroot commented Nov 27, 2024

I have similar glitches in the same situation on sdk 52 with the new architecture enabled. When I disable the new architecture it works much better but still I get the same messages as @fonodi :

Also I'm seeing a lot of console errors during the dragging.

(NOBRIDGE) WARN  [Reanimated] Tried to modify key `current` of an object which has been already passed to a worklet. See
https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#tried-to-modify-key-of-an-object-which-has-been-converted-to-a-shareable
for more details.

@MeNoKVN
Copy link

MeNoKVN commented Nov 28, 2024

I am also having issues since upgrading to EXPO SDK 52.

This setup doesnt work anymore. The animations are broken and I am getting this error:

(NOBRIDGE) ERROR Warning: ref.measureLayout must be called with a ref to a native component.

@ddrozdov
Copy link

ddrozdov commented Dec 2, 2024

Managed to fix the same issue in my project with this patch, please check if it helps.

react-native-draggable-flatlist+4.0.1.patch.txt

@ddrozdov
Copy link

ddrozdov commented Dec 2, 2024

@MeNoKVN

(NOBRIDGE) ERROR Warning: ref.measureLayout must be called with a ref to a native component.

See #544

@aleksey-mukho
Copy link

Managed to fix the same issue in my project with this patch, please check if it helps.

react-native-draggable-flatlist+4.0.1.patch.txt

I applied your patch, now It's not so horrible, but I still can see some glitches...

@kristof-kovacs
Copy link
Author

@ddrozdov thank you for the patch! I went ahead and applied it and as @aleksey-mukho noted, there has been an improvement but not all the way.

In detail:
The Good: The items flashing back to their original spot is indeed fixed by your patch. This is great step forward.

The Bad: However, now the items themselves very briefly flash on rearrange. Sometimes multiple items, not just the ones being dragged. To me this suggests that the draggable flatlist is being re-rendered.

The Ugly: The resizing issue is still present, and I still cannot reproduce it consistently. Sometimes I will drag an item and it will drop down and resize down correctly. Other times, I will drag and drop, but the item never resizes down again.

@BenGroot
Copy link

Hi @computerjazz Your project is amazing, but I think everyone is missing you 😃 Could you tell us what your plans are with the project?

@fonodi
Copy link

fonodi commented Dec 11, 2024

Hi @computerjazz Your project is amazing, but I think everyone is missing you 😃 Could you tell us what your plans are with the project?

He mentioned his plans here #542 (comment)

@Fjort
Copy link

Fjort commented Dec 14, 2024

Has anyone managed to solve the problem?

@Fjort
Copy link

Fjort commented Dec 14, 2024

I have identified the cause of the problem. In the CellRenderer Component, the transform style is updated while the element is being moved.
After changing the data, new data comes to onDragEnd, but the elements that match the offset keys still have the transform style, which is then reset to 0.
I have not been able to solve this problem yet, but I continue to figure it out.

@ddrozdov
Copy link

ddrozdov commented Dec 16, 2024

The Bad: However, now the items themselves very briefly flash on rearrange. Sometimes multiple items, not just the ones being dragged. To me this suggests that the draggable flatlist is being re-rendered.

@kristof-kovacs I just noticed the same when in onDragEnd I updated my state with exactly the data I got from the library, but if I do data.map(item => ({ ...item })) the flash goes away. No explanation why, it just works for me :)

@Fjort
Copy link

Fjort commented Dec 16, 2024

@kristof-kovacs I just noticed the same when in onDragEnd I updated my state with exactly the data I got from the library, but if I do data.map(item => ({ ...item })) the flash goes away. No explanation why, it just works for me :)

It didn't help me ((

harveyappleton added a commit to tradecert/react-native-draggable-flatlist that referenced this issue Dec 18, 2024
@kristof-kovacs
Copy link
Author

kristof-kovacs commented Dec 20, 2024

@ddrozdov this actually helped for me as well for one moment, but then it reverted to the same behavior. So unfortunately not this did not fix it.

However, I still am running into the issue of some of the items that I move are resized wrong and do not size down after drag

@Hormold
Copy link

Hormold commented Dec 24, 2024

I also have the same problem. Even in the example, empty app. Should I try downgrading the Expo version?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests