React Native报错:FlatList按item.id播放动画时出现singleValue.stopTracking错误
问题分析与解决方案
错误原因
你遇到的Undefined is not an object (evaluating 'singleValue.stopTracking')错误,核心问题出在动画状态的管理方式上:
- 初始
state里的progress是单个Animated.Value(0),而非存储每个item对应动画值的对象,所以当你尝试访问this.state.progress[id]时,得到的是undefined,传给Animated.timing自然会报错。 - 你的
anim_star方法里,用this.setState({ progress: new Animated.Value(id) })直接替换了整个progress状态,把原本应该是键值对的结构改成了单个动画值,彻底打乱了多item动画的管理逻辑。
修复步骤与代码示例
1. 修正初始状态的progress结构
把progress改成空对象,用来存储每个item.id对应的Animated.Value:
constructor(props) { super(props); this.state = { isLoading: true, id: '', dataSource: '', progress: {}, // 改为空对象,key为item.id,value为对应动画值 }; }
2. 重构anim_star方法
确保每个item的动画值存在,然后执行动画,同时在动画结束后重置值以便下次播放:
import { Animated, Easing, Alert } from 'react-native'; anim_star = (id) => { Alert.alert(id); // 先获取当前的progress对象,确保目标id的动画值已初始化 const { progress } = this.state; let targetProgress = progress[id]; // 如果该id还没有对应的动画值,就创建一个并更新状态 if (!targetProgress) { targetProgress = new Animated.Value(0); this.setState({ progress: { ...progress, [id]: targetProgress } }); } // 重置动画值到0,保证每次点击都能从头播放 targetProgress.setValue(0); // 执行动画 Animated.timing(targetProgress, { toValue: 1, duration: 2000, easing: Easing.linear, useNativeDriver: false, // 根据你的Animation组件是否支持原生驱动调整 }).start(); }
3. 调整FlatList的渲染逻辑
确保每个Animation组件能拿到对应id的动画值,避免渲染时出现undefined:
renderItem={({ item }) => { // 提前初始化当前item的动画值(如果还没初始化的话) const { progress } = this.state; if (!progress[item.id]) { this.setState(prevState => ({ progress: { ...prevState.progress, [item.id]: new Animated.Value(0) } })); } return ( <View> <Card> <View rkCardFooter> <TouchableOpacity onPress={() => this.anim_star(item.id)} style={{ position: 'absolute', height: '100%', width: '100%' }} > <Animation progress={progress[item.id] || new Animated.Value(0)} source={require('../Animations/favourite_app_icon.json')} style={{ height: '100%', width: '100%', position: 'absolute' }} resizeMode="contain" /> </TouchableOpacity> <Text> Sample</Text> </View> </Card> </View> ); }}
4. 可选优化:批量初始化动画值
如果你的dataSource是异步加载的,可以在数据加载完成后批量初始化所有item的动画值,避免渲染时频繁触发setState:
componentDidUpdate(prevProps, prevState) { // 当dataSource更新时,批量初始化所有item的动画值 if (prevState.dataSource !== this.state.dataSource && this.state.dataSource) { const newProgress = {}; this.state.dataSource.forEach(item => { newProgress[item.id] = new Animated.Value(0); }); this.setState({ progress: newProgress }); } }
关键注意点
- 每个item的动画值需要独立存储,不能共用一个
Animated.Value,否则会出现所有item动画同步播放的问题。 - 动画结束后记得重置
Animated.Value到0,否则再次点击时,因为当前值已经是toValue的目标值,动画不会触发。
内容的提问来源于stack exchange,提问作者Laney Williams
相关产品推荐
相关产品推荐

