Home > Mobile >  Changed from react native class to function and it doesnt work 100%
Changed from react native class to function and it doesnt work 100%

Time:01-13

I am trying to change a class into a function. This is mainly because if I can get it working, I want to use it for learning different animations, I have had some success but not 100%. Originally it displayed an icon that when clicked it spun it one way and then when clicked again it spun the other way. What I have tried to do it get rid of the icon and replace it with an image. It works when clicked once but then does nothing.

I am struggling with toggled aspect of it and setting the state I think because I cant seem to set it up properly in a function.

I have tried several things but this is the best I can get. If I show the original code and then what I have managed to change, maybe someone can point me in the right direction as to what I am doing wrong.

All I want is the image to display and then when clicked spins right and then if clicked again it spins left.

I am doing this so I can mess around with the settings and hopefully learn animation a bit better.

Any help would be greatly appreciated.

The original code :

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,
  },
});

and what I have changed so far :

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;

CodePudding user response:

There are a few issues. You first had menuToggled initialized to [null] when it should have been null. You also had forgotten to use onPress in TabIcon. The most noteworthy thing was wrapping TabIcon in a useCallback to prevent it from being recreated all the time. Expo snack:

import React, { useRef, useState, useCallback } from 'react';
import {
  View,
  StyleSheet,
  Animated,
  Image,
  TouchableOpacity,
} from 'react-native';
import Constants from 'expo-constants';

const App = () => {
  const spinValue = useRef(new Animated.Value(0)).current;
  const [menuToggled, setMenuToggled] = useState(null);

  const TabIcon = useCallback(({ onPress, menuToggled }) => {
    const logoStyles = [styles.logoStyle];
    // initialized base on menuToggled
    // if not done then it will take an additional button press to trigger
    // the animation
    const animation = useRef(new Animated.Value(menuToggled ? 0 : 1)).current;

    const startAnimation = () => {
      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
        onPress={() => {
          startAnimation();
          onPress?.();
        }}>
        <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}>
      <TabIcon
        onPress={() => setMenuToggled((prev) => !prev)}
        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;

  • Related