React Native旧Animated迁移Reanimated:点击动画无法隔离单个项
问题解决:Reanimated迁移后FlatList点击项无法隔离动画
问题原因
你当前代码的核心问题是:rotation这个useSharedValue定义在App根组件中,所有FlatList的Item组件共享同一个动画状态值。点击任意项时修改的都是同一个全局rotation,自然所有项会同步触发动画——这和旧Animated代码里每个Item独立维护Animated.Value的逻辑完全相反,旧代码的隔离性正是源于每个Item拥有专属的动画状态。
修复方案
把动画相关的状态与逻辑移至Item组件内部,让每个Item拥有独立的共享值和动画处理函数,点击单个Item时仅修改自身的动画状态,不会影响其他项。同时修正keyExtractor的错误(原写法会导致列表key无效)。
修改后的完整代码
import React from 'react'; import { SafeAreaView, FlatList, View, Text } from 'react-native'; import Animated, { useSharedValue, useAnimatedStyle, withSequence, withTiming, withRepeat, Easing } from 'react-native-reanimated'; const ANGLE = 10; const TIME = 100; const EASING = Easing.elastic(1.5); const items = [ { id: 1, name: 'name 1' }, { id: 2, name: 'name 2' }, { id: 3, name: 'name 3' }, { id: 4, name: 'name 4' }, ]; // 独立封装Item组件,内部维护专属动画状态 const Item = ({ item }) => { // 每个Item实例拥有独立的共享值 const rotation = useSharedValue(0); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ rotateZ: `${rotation.value}deg` }], })); const handlePress = () => { rotation.value = withSequence( withTiming(-ANGLE, { duration: TIME / 2, easing: EASING }), withRepeat( withTiming(ANGLE, { duration: TIME, easing: EASING, }), 7, true ), withTiming(0, { duration: TIME / 2, easing: EASING }) ); }; return ( <View style={{ margin: 10 }}> <Animated.View style={animatedStyle}> <Text onPress={handlePress}>{item.name}</Text> </Animated.View> </View> ); }; export default function App() { return ( <SafeAreaView style={{ flex: 1 }}> <FlatList numColumns={4} data={items} renderItem={({ item }) => <Item item={item} />} keyExtractor={item => item.id.toString()} // 使用唯一id作为列表项key /> </SafeAreaView> ); }
关键修改点
- 动画状态私有化:将
rotation共享值、animatedStyle、handlePress全部移入Item组件,确保每个Item实例拥有独立的动画状态。 - 修正列表key:把无效的
keyExtractor改为使用item的唯一id,保证FlatList的正确渲染与项复用。 - 组件结构优化:将Item组件从App内部抽离,符合组件封装逻辑,避免内部组件重复创建的性能问题。
内容的提问来源于stack exchange,提问作者JulesUK
相关产品推荐
相关产品推荐

