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

Reanimated动画视图不响应值变更问题排查求助

React Native Reanimated 动画视图不响应共享值更新问题

问题概述

实现了一个包含按钮和动画矩形的示例:点击按钮会随机修改offset共享值,预期蓝色矩形会随值变化移动,但实际点击后无任何反应。共享值和动画样式已确认更新,但视图未响应。相同代码在其他项目部分文件有效,无法定位原因。

复现步骤:

  • 点击灰色按钮区域
  • 观察蓝色矩形位置

环境信息:

  • Reanimated 版本:2.11.0
  • React Native 版本:0.70.3
  • 涉及平台:Android、iOS
  • 测试设备:iOS模拟器

复现代码

import React from 'react';
import {View, TouchableOpacity, Animated, Text} from 'react-native';

import {useAnimatedStyle, useSharedValue} from 'react-native-reanimated';

const App = () => {
  const offset = useSharedValue({x: 0, y: 0});
  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{translateX: offset.value.x}, {translateY: offset.value.y}],
  }));

  const Button = () => (
    <TouchableOpacity
      onPress={() => {
        offset.value = {
          x: Math.random() * 100,
          y: Math.random() * 100,
        };
      }}>
      <View style={{width: 500, height: 500, backgroundColor: 'grey'}} />
    </TouchableOpacity>
  );
  const AnimatedRectangle = () => (
    <Animated.View
      style={[
        animatedStyle,
        {
          width: 100,
          height: 100,
          backgroundColor: 'blue',
        },
      ]}>
      <Text style={{color: 'white'}}>Why I'm not moving?</Text>
    </Animated.View>
  );

  return (
    <View style={{flex: 1}}>
      <Button />
      <AnimatedRectangle />
    </View>
  );
};

export default App;

问题根源

  1. 跨组件传递useAnimatedStyle生成的样式:animatedStyle在父组件App中创建,却在内部子组件AnimatedRectangle中使用。Reanimated 2的useAnimatedStyle依赖组件作用域绑定,跨组件传递会导致无法追踪共享值更新,进而无法触发视图重绘。
  2. 内部组件重复创建:Button和AnimatedRectangle定义在App内部,每次App渲染都会重新创建这两个组件,干扰Reanimated的状态追踪逻辑。

修复方案

将子组件提取到外部,并在AnimatedRectangle内部创建useAnimatedStyle,通过props传递共享值:

import React from 'react';
import {View, TouchableOpacity, Animated, Text} from 'react-native';

import {useAnimatedStyle, useSharedValue} from 'react-native-reanimated';

// 提取Button为独立组件,接收点击回调
const Button = ({ onPress }) => (
  <TouchableOpacity onPress={onPress}>
    <View style={{width: 500, height: 500, backgroundColor: 'grey'}} />
  </TouchableOpacity>
);

// 提取AnimatedRectangle为独立组件,接收共享值offset
const AnimatedRectangle = ({ offset }) => {
  // 在组件内部创建动画样式,确保作用域绑定
  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{translateX: offset.value.x}, {translateY: offset.value.y}],
  }));
  
  return (
    <Animated.View
      style={[
        animatedStyle,
        { width: 100, height: 100, backgroundColor: 'blue' }
      ]}>
      <Text style={{color: 'white'}}>Now I can move!</Text>
    </Animated.View>
  );
};

const App = () => {
  const offset = useSharedValue({x: 0, y: 0});

  return (
    <View style={{flex: 1}}>
      <Button 
        onPress={() => {
          offset.value = {
            x: Math.random() * 100,
            y: Math.random() * 100,
          };
        }} 
      />
      <AnimatedRectangle offset={offset} />
    </View>
  );
};

export default App;

关键修改点:

  • 把内部组件移到App外部,避免每次渲染重复创建。
  • 在AnimatedRectangle内部使用useAnimatedStyle,确保动画样式与Animated.View同作用域,让Reanimated能正确追踪共享值变化。
  • 通过props传递点击回调和共享值,保持状态管理在父组件。

补充提示

如果需要在父组件创建动画样式,可结合React.memo对子组件进行 memoization,但最稳妥的方式还是让useAnimatedStyle和Animated.View处于同一组件作用域,这是Reanimated 2的设计要求。

内容的提问来源于stack exchange,提问作者Alex Tumanov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:40:19