微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如果状态发生变化,动画将无法开始播放供演示的小点心

如何解决如果状态发生变化,动画将无法开始播放供演示的小点心

Snack is here

你好,我很难解决一个愚蠢的问题,而且我变得发疯了。

我只是想在屏幕聚焦(在标签栏导航中)时制作一个简单而优雅的动画。我的点心可以很好地直到我在屏幕上执行状态更改。然后,即使调用并执行了来自焦点侦听器的回调(检查日志),动画也不会开始... 为什么?

我制作了一个按钮来手动触发动画...并且它可以正常工作! 我想我把小吃弄清楚了,但是如果您需要更多信息,请问我。我求求你,请帮助一个绝望的兄弟。

Snack is here

如果您不愿意点击“小吃”:

import React,{ useState,useEffect } from "react";
import { Text,View,Animated,Dimensions,StyleSheet,SafeAreaView,Button } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

function HomeScreen({navigation}) {
  const initialXPos = Dimensions.get("window").height * 0.5 ;
  const xPos = new Animated.Value(initialXPos);
  const opacity = new Animated.Value(0);
  const [change,setChange] = useState(true)

   useEffect(() => {
    const unsubscribe = navigation.addListener("focus",comingFromBot);
    return unsubscribe;
  },[]);

  const comingFromBot = () => {
    xPos.setValue(initialXPos);
    opacity.setValue(0);
    Animated.parallel([
      Animated.spring(xPos,{
        tovalue: 100,tension:3,useNativeDriver: true,}),Animated.timing(opacity,{
        tovalue: 1,duration: 1000,]).start();
    console.log("Animation's Fired!");
  };

  return (
    <SafeAreaView style={{flex:1}}>
      <Animated.View style={[
            styles.container,{ transform: [{ translateY: xPos }] },{ opacity: opacity },]}>
        <Text style={{fontSize:30}}>{change ? "Home!" : "TIMMY!"}</Text>
      </Animated.View>

      {/* debug */}
      <View style={styles.fire}>
        <Button title="fire" onPress={() => comingFromBot()}/>
      </View>

      <View style={styles.change}>
        <Button title="change" onPress={() => setChange(!change)}/>
      </View>
    </SafeAreaView>
  );
}
const styles = StyleSheet.create({
  container: { flex: 1,alignItems: 'center' },fire:{position:"absolute",width:"100%",bottom:0},change:{position:"absolute",bottom:48}
  });

function SettingsScreen() {
  return (
    <View style={{ flex: 1,justifyContent: 'center',alignItems: 'center',padding:8 }}>
      <Text>{"Go to Home tab again,and notice the animation.\n\nEXCEPT if we changed the text... WHY?\n\nBUT still works if we fire the animation with the button,but after still won't work on focus detection... HOW?\n\nWorks if you hot reload / hard reload the app... HELP?"}</Text>
    </View>
  );
}

const Tab = createBottomTabNavigator();

function MyTabs() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );
}

export default function App() {
  return (
    <NavigationContainer>
      <MyTabs />
    </NavigationContainer>
  );
}

解决方法

该操作无效,因为您未遵循rules of hooks。您的代码中有以下错误:

  1. 您正在useEffect钩中使用外部变量,但传递了空的依赖项数组
  2. 动画值必须位于useStateuseRef钩中,以便不会在每次渲染时都重新创建它们。

然后,即使调用并执行了来自焦点侦听器的回调(检查日志),动画也将无法启动...为什么?

问题在于在重新渲染状态更新后会重新创建回调,因此传递给焦点侦听器的回调不再与渲染中的回调相同。并且由于您也没有在state / ref中设置动画值,因此在旧焦点侦听器引用旧值时也会创建新动画值。基本上,您看到的日志来自旧的侦听器,而不是新的侦听器。

您应该使用official eslint plugin并确保从中解决所有警告/错误,从而避免出现此类问题。

要修复您的代码,请进行以下更改:

const [xPos] = React.useState(() => new Animated.Value(initialXPos));
const [opacity] = React.useState(() => new Animated.Value(0));
const [change,setChange] = useState(true)

useEffect(() => {
  const unsubscribe = navigation.addListener("focus",comingFromBot);
  return unsubscribe;
},[navigation,comingFromBot]);

const comingFromBot = useCallback(() => {
  xPos.setValue(initialXPos);
  opacity.setValue(0);
  Animated.parallel([
    Animated.spring(xPos,{
      toValue: 100,tension:3,useNativeDriver: true,}),Animated.timing(opacity,{
      toValue: 1,duration: 1000,]).start();
  console.log("Animation's Fired!");
},[xPos,opacity]);

我基本上添加了useCallback,修复了依赖项数组,并将动画值移动到了useState钩子上。

,

由于@ satya164:Snack

,我终于以这一点结束了

我也希望我以前在documentation中读过这篇文章。

HomeScreen的代码:

// HomeScreen.js
function HomeScreen({navigation}) {
  const initialXPos = Dimensions.get("window").height * 0.5 ;
  const xPos = useRef(new Animated.Value(initialXPos)).current 
  const opacity = useRef(new Animated.Value(0)).current 
  const [change,setChange] = useState(true)

  useEffect(() => {
    const unsubscribe = navigation.addListener("focus",comingFromBot);
    return unsubscribe;
  },comingFromBot]);

  const comingFromBot = useCallback(() => {
    xPos.setValue(initialXPos);
    opacity.setValue(0);
    Animated.parallel([
      Animated.spring(xPos,{
        toValue: 100,{
        toValue: 1,]).start();
    console.log("Animation's Fired!");
  },opacity,initialXPos ]);

  return (
    <SafeAreaView style={{flex:1}}>
      <Animated.View style={[
            styles.container,{ transform: [{ translateY: xPos }] },{ opacity: opacity },]}>
        <Text style={{fontSize:30}}>{change ? "Home!" : "TIMMY!"}</Text>
      </Animated.View>

      {/* debug */}
      <View style={styles.fire}>
        <Button title="fire" onPress={() => comingFromBot()}/>
      </View>

      <View style={styles.change}>
        <Button title="change" onPress={() => setChange(!change)}/>
      </View>
    </SafeAreaView>
  );
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。