React Native类组件转函数组件后旋转动画Toggle失效求助
解决React Native函数组件中图片交替旋转的Toggle逻辑问题
问题描述
将React Native类组件转换为函数组件时,替换图标为图片后仅首次点击能触发旋转,后续点击无反应,需修复Toggle逻辑,实现图片点击交替顺时针、逆时针旋转的效果。
原类组件代码
import React from 'react'; import { View, StyleSheet, Animated, Image, TouchableOpacity } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; const TabIcon = ({ onPress, menuToggled }) => { const logoStyles = [styles.logoStyle]; if (menuToggled !== null) { const animation = new Animated.Value(menuToggled ? 0 : 1); Animated.timing(animation, { toValue: menuToggled ? 1 : 0, duration: 500, useNativeDriver: true }).start(); const rotateInterpolate = animation.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '360deg'] }); const animatedStyles = { transform: [{ rotate: rotateInterpolate }] }; logoStyles.push(animatedStyles); } return ( <TouchableOpacity style={styles.tabStyle} onPress={onPress} > <Animated.View style={logoStyles}> <Animated.Image style={styles.tinyLogo} source={{ uri: 'https://reactnative.dev/img/tiny_logo.png', }} /> </Animated.View> </TouchableOpacity> ); }; export default class App extends React.Component { state = { menuToggled: null } toggleMenu = () => { this.setState(prevState => { return { menuToggled: !prevState.menuToggled }; }); } render () { return ( <View style={styles.container}> <TabIcon onPress={this.toggleMenu} menuToggled={this.state.menuToggled} /> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'white' }, tinyLogo: { width: 150, height: 150, borderRadius: 100, margin: 8, }, });
修改后的函数组件代码(存在问题)
import React, { useRef, useState } from "react"; import { View, StyleSheet, Animated, Image, TouchableOpacity, Easing } from 'react-native'; import Constants from 'expo-constants'; const App = () => { const spinValue = useRef(new Animated.Value(0)).current; const [menuToggled, setMenuToggled] = useState([null]); toggleMenu = () => { setMenuToggled(menuToggled === "null" ? "menuToggled" : "null"); } const Spinner = ({ onPress, menuToggled }) => { const logoStyles = [styles.logoStyle]; const animation = new Animated.Value(0); const go = () => { Animated.timing(animation, { toValue: 1, duration: 1500, easing: Easing.elastic(1), useNativeDriver: true }).start(); } const rotateInterpolate = animation.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '360deg'] }); const animatedStyles = { transform: [{ rotate: rotateInterpolate }] }; logoStyles.push(animatedStyles); return ( <TouchableOpacity onPress={go} > <Animated.View style={logoStyles}> <Animated.Image style={styles.tinyLogo} source={{ uri: 'https://reactnative.dev/img/tiny_logo.png', }} /> </Animated.View> </TouchableOpacity> ); }; return ( <View style={styles.container}> <Spinner onPress={toggleMenu} menuToggled={menuToggled} /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'white' }, tinyLogo: { width: 150, height: 150, borderRadius: 100, margin: 8, }, }); export default App;
修复方案及代码
问题分析
- 状态初始化错误:
useState([null])将初始状态设为数组,后续状态判断逻辑混乱,应改为布尔值类型的初始状态null或false。 - Toggle逻辑错误:
toggleMenu中用字符串"null"和"menuToggled"判断,不符合布尔值切换逻辑,应使用函数式更新切换布尔状态。 - 动画值未持久化:
Spinner组件内每次渲染都创建新的Animated.Value,导致动画无法复用,需用useRef保存动画值。 - 点击事件未绑定状态切换:
Spinner的TouchableOpacity仅调用go函数,未触发父组件的onPress切换状态,导致旋转方向无法交替。 - 旋转方向未区分:未根据
menuToggled状态设置不同的旋转方向,无法实现交替顺逆时针旋转。
修复后的完整代码
import React, { useRef, useState } from "react"; import { View, StyleSheet, Animated, Image, TouchableOpacity, Easing } from 'react-native'; const App = () => { // 初始化布尔状态,控制旋转方向 const [menuToggled, setMenuToggled] = useState(null); // 切换状态的函数,使用函数式更新确保获取最新状态 const toggleMenu = () => { setMenuToggled(prev => !prev); }; const Spinner = ({ onPress, menuToggled }) => { // 用useRef持久化动画值,避免每次渲染重置 const animation = useRef(new Animated.Value(0)).current; const startAnimation = () => { // 先调用父组件的状态切换函数 onPress(); // 根据当前状态设置目标值和旋转方向 const toValue = menuToggled ? 0 : 1; Animated.timing(animation, { toValue, duration: 1500, easing: Easing.elastic(1), useNativeDriver: true }).start(); }; // 根据menuToggled状态切换旋转方向:顺时针360deg / 逆时针-360deg const rotateInterpolate = animation.interpolate({ inputRange: [0, 1], outputRange: menuToggled ? ['0deg', '-360deg'] : ['0deg', '360deg'] }); const animatedStyles = { transform: [{ rotate: rotateInterpolate }] }; return ( <TouchableOpacity onPress={startAnimation}> <Animated.View style={[styles.logoStyle, animatedStyles]}> <Animated.Image style={styles.tinyLogo} source={{ uri: 'https://reactnative.dev/img/tiny_logo.png', }} /> </Animated.View> </TouchableOpacity> ); }; return ( <View style={styles.container}> <Spinner onPress={toggleMenu} menuToggled={menuToggled} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'white' }, tinyLogo: { width: 150, height: 150, borderRadius: 100, margin: 8, }, }); export default App;
关键修改说明
- 状态改为布尔值类型,通过
setMenuToggled(prev => !prev)可靠切换状态。 - 用
useRef保存Animated.Value,确保组件渲染时动画值不会被重置。 - 点击事件先触发状态切换,再根据最新状态执行对应方向的动画。
- 通过
menuToggled动态设置outputRange,实现顺时针/逆时针交替旋转。
内容的提问来源于stack exchange,提问作者JulesUK
相关产品推荐
相关产品推荐

