-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Description
The issue was originally reported by @efstathiosntonas on the react-native-sortables repository (here). This regression was introduced after removing style flattening in this PR and then cherry-picked to 3.17.2 in this PR.
Basically, the issue can be seen if the animated view is passed more than one animated style (the additional style can be even empty). When a view with such styles re-render, the style from the initial style updater run is applied to the view instead of the last returned style. This is best visible if the useAnimatedStyle hook is used to create a discrete style updated based a condition (e.g. a Shared Value is a flag indicating whether to return one or the other style) and doesn't contain animations.
The issue can be fixed by just adding the style flattening in this line as follows:
props[key] = StyleSheet.flatten(processedStyle);Issue recording
Observe the logs. You can see, that when re-render happens, the width of the box changes, even though the value returned within the useAnimatedStyle callback doesn't change.
incorrect.mp4
Expected behavior
After bringing back the style flattening
valid.mp4
Steps to reproduce
- Copy code from the expo snack or from below
- Paste the code to the react-native app with reanimated 3.17.2 or 3.17.3
- Observe that the width of the view is set back to
100after re-render
Source code
import { useEffect, useState } from 'react';
import { StyleSheet, View } from 'react-native';
import Animated, {
LinearTransition,
useAnimatedStyle,
useSharedValue,
} from 'react-native-reanimated';
export default function PlaygroundExample() {
const [_, setCount] = useState(0);
const animatedWidth = useSharedValue(0);
const animatedWidthStyle = useAnimatedStyle(() => {
if (animatedWidth.value === 0) {
console.log('predefined width');
return { width: 100 };
}
console.log('animatedWidth.value', animatedWidth.value);
return {
width: animatedWidth.value,
};
});
const emptyAnimatedStyle = useAnimatedStyle(() => ({}));
useEffect(() => {
animatedWidth.value = 10;
setTimeout(() => {
console.log('re-render');
setCount((prev) => prev + 1);
}, 1000);
setTimeout(() => {
animatedWidth.value = 20;
}, 4000);
}, [animatedWidth]);
return (
<Animated.View
layout={LinearTransition}
style={[emptyAnimatedStyle, animatedWidthStyle]}>
<View style={styles.box} />
</Animated.View>
);
}
const styles = StyleSheet.create({
box: {
backgroundColor: 'red',
height: 100,
},
});Snack or a link to a repository
https://snack.expo.dev/vZOtho0RKCBlZNXu4uI1q
Reanimated version
3.17.2
React Native version
0.79.0
Platforms
Android, iOS
JavaScript runtime
None
Workflow
None
Architecture
Paper (Old Architecture)
Build type
None
Device
None
Device model
No response
Acknowledgements
Yes