You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native报错:FlatList按item.id播放动画时出现singleValue.stopTracking错误

问题分析与解决方案

错误原因

你遇到的Undefined is not an object (evaluating 'singleValue.stopTracking')错误,核心问题出在动画状态的管理方式上:

  1. 初始state里的progress是单个Animated.Value(0),而非存储每个item对应动画值的对象,所以当你尝试访问this.state.progress[id]时,得到的是undefined,传给Animated.timing自然会报错。
  2. 你的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 03:27:35