software-mansion / react-native-reanimated

React Native's Animated library reimplemented
https://docs.swmansion.com/react-native-reanimated/
MIT License
8.66k stars 1.27k forks source link

[Android-IOS] [3.11.0] Animation update incorrect when setState after set shared value #6002

Closed ngocle2497 closed 2 months ago

ngocle2497 commented 2 months ago

Description

When i call setState after update reanimated value, animated view update incorrect Reanimared 3.8.1 work correctly for both android and ios Case 1: use useAnimatedStyle -> Animated view stuck on multiple view Case 2: use inline style -> Animated view delay update

https://github.com/software-mansion/react-native-reanimated/assets/43195241/8a9fdc80-3292-463a-aba7-7dd5063fd4b3

https://github.com/software-mansion/react-native-reanimated/assets/43195241/01c3cdd7-5013-4399-a643-955f58514936

Steps to reproduce

  1. Copy my code to your project
    
    import React, {useEffect, useState} from 'react';
    import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
    import Animated, {
    SharedValue,
    useAnimatedStyle,
    useSharedValue,
    } from 'react-native-reanimated';

const Item = ({ position, index, onPress, }: { position: SharedValue; index: number; onPress: (index: number) => void; }) => { const handlePress = () => { onPress(index); };

const style = useAnimatedStyle(() => { return { backgroundColor: position.value === index ? '#3498db' : 'transparent', borderBottomColor: position.value === index ? '#3498db' : 'transparent', }; });

return (

Item: {index}

); };

const App = () => { const position = useSharedValue(0); const [selectedIndex, setSelectedIndex] = useState(position.value);

const onPress = (index: number) => { position.value = index; setSelectedIndex(index); };

const renderItem = (item: number) => { return (

);

};

useEffect(() => { // TODO: call api or some effect }, [selectedIndex]);

return (

{[0, 1, 2, 3, 4, 5].map(renderItem)}

); };

const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, rowItem: { flexDirection: 'row', columnGap: 4, width: '100%', height: 100, }, item: { height: 100, width: 50, borderBottomWidth: 2, borderColor: 'transparent', overflow: 'hidden', }, });

export default App;


Run your app

### Snack or a link to a repository

See my code below

### Reanimated version

3.11.0

### React Native version

0.73.8

### Platforms

Android, iOS

### JavaScript runtime

Hermes

### Workflow

React Native

### Architecture

Paper (Old Architecture)

### Build type

Debug app & dev bundle

### Device

iOS simulator

### Device model

Iphone 15 Pro Max

### Acknowledgements

Yes
github-actions[bot] commented 2 months ago

Hey! 👋

The issue doesn't seem to contain a minimal reproduction.

Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem?

szydlovsky commented 2 months ago

@ngocle2497 Hello! This is a really interesting issue. Let me explain it: In your example, setting a value of a SharedValue is a asynchronous operation - its value change is scheduled and it takes around 1-2 frames to get the value on both threads. Meanwhile, you change the React state immediately after it, expecting that the new render (caused by the changed state) will contain the new SharedValue value - which isn't there because it is scheduled a little bit later. Such situations can be nicely fixed by usage of react-native-gesture-handler, which offers handling the gestures reactively and is fully integrated with Reanimated. I have re-written your example using it (keep in mind that your app needs to have a GestureHandlerRootView at its root for the gestures to work - see GH installation guide):

Fixed example code:

Code ``` TYPESCRIPT import React, { useEffect, useState } from 'react'; import { StyleSheet, Text, View } from 'react-native'; import Animated, { SharedValue, useAnimatedStyle, useSharedValue, runOnJS, } from 'react-native-reanimated'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; const Item = ({ position, index, }: { position: SharedValue; index: number; }) => { const style = useAnimatedStyle(() => { return { backgroundColor: position.value === index ? '#3498db' : 'transparent', borderBottomColor: position.value === index ? '#3498db' : 'transparent', }; }); return ( Item: {index} ); }; const App = () => { const position = useSharedValue(0); const [selectedIndex, setSelectedIndex] = useState(position.value); const renderItem = (item: number) => { const tap = Gesture.Tap().onEnd(() => { position.value = item; runOnJS(setSelectedIndex)(item); }); return ( ); }; useEffect(() => { // TODO: call api or some effect }, [selectedIndex]); return ( {[0, 1, 2, 3, 4, 5].map(renderItem)} ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, rowItem: { flexDirection: 'row', columnGap: 4, width: '100%', height: 100, }, item: { height: 100, width: 50, borderBottomWidth: 2, borderColor: 'transparent', overflow: 'hidden', }, }); export default App; ```
ngocle2497 commented 2 months ago

@szydlovsky thank for explain. I discovered this issue when running the project on production environment. Actually, it's not just an example, the example just describes how to make an error. in my project i have 1 swipeable list. When entering a menu in a swipeable section, I also use a gesture handler (tap gesture) to close the swipeable action after switching screens. And you know, when switching screens, setstate happens very often (turn on loading, update data,...). that is swipeable and not closeable. I will upload a video of my today's production project around today (GMT +7). Hope you can fix this error for me. Thank

ngocle2497 commented 2 months ago

https://github.com/software-mansion/react-native-reanimated/assets/43195241/2c7fc8a8-e364-42dd-b1c9-6d13ede8a66d

https://github.com/software-mansion/react-native-reanimated/assets/43195241/ddb4518e-547d-495c-a66c-9178c8cb8b36 I also use react navigation native stack @szydlovsky can u open this issue by my video? issue 1: Tab alert not active Issue 2: swipeable cannot closeable

szydlovsky commented 2 months ago

@ngocle2497 Hi again. Unfortunately, the example video is not enough te re-open the issue. I know that we cannot access the source code, because it is a private company project, but that leaves us with only guessing what could be wrong. You can try making a more complex repro out of it and posting it here - then I will reopen the issue. Other than that, I can give you some tips to check in your code: