diff --git a/src/components/FloatingActionButton.js b/src/components/FloatingActionButton.js index c49f69c336eb..e29a06ffc3d3 100644 --- a/src/components/FloatingActionButton.js +++ b/src/components/FloatingActionButton.js @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; -import React, {PureComponent} from 'react'; +import React, {memo, useCallback, useEffect, useRef} from 'react'; import {Animated, Easing, View} from 'react-native'; +import usePrevious from '@hooks/usePrevious'; import compose from '@libs/compose'; import * as StyleUtils from '@styles/StyleUtils'; import Icon from './Icon'; @@ -36,81 +37,78 @@ const defaultProps = { buttonRef: () => {}, }; -class FloatingActionButton extends PureComponent { - constructor(props) { - super(props); - this.animatedValue = new Animated.Value(props.isActive ? 1 : 0); - } - - componentDidUpdate(prevProps) { - if (prevProps.isActive === this.props.isActive) { - return; - } - - this.animateFloatingActionButton(); - } +function FloatingActionButton(props) { + const animatedValue = useRef(new Animated.Value(props.isActive ? 1 : 0)); + const previousIsActive = usePrevious(props.isActive); + const fabPressable = useRef(null); /** * Animates the floating action button * Method is called when the isActive prop changes */ - animateFloatingActionButton() { - const animationFinalValue = this.props.isActive ? 1 : 0; - - Animated.timing(this.animatedValue, { + const animateFloatingActionButton = useCallback(() => { + const animationFinalValue = props.isActive ? 1 : 0; + Animated.timing(animatedValue.current, { toValue: animationFinalValue, duration: 340, easing: Easing.inOut(Easing.ease), useNativeDriver: false, }).start(); - } - - render() { - const rotate = this.animatedValue.interpolate({ - inputRange: [0, 1], - outputRange: ['0deg', '135deg'], - }); - - const backgroundColor = this.animatedValue.interpolate({ - inputRange: [0, 1], - outputRange: [this.props.theme.success, this.props.theme.buttonDefaultBG], - }); - - const fill = this.animatedValue.interpolate({ - inputRange: [0, 1], - outputRange: [this.props.theme.textLight, this.props.theme.textDark], - }); - - return ( - - - { - this.fabPressable = el; - if (this.props.buttonRef) { - this.props.buttonRef.current = el; - } - }} - accessibilityLabel={this.props.accessibilityLabel} - role={this.props.role} - pressDimmingValue={1} - onPress={(e) => { - // Drop focus to avoid blue focus ring. - this.fabPressable.blur(); - this.props.onPress(e); - }} - onLongPress={() => {}} - style={[this.props.themeStyles.floatingActionButton, StyleUtils.getAnimatedFABStyle(rotate, backgroundColor)]} - > - - - - - ); - } + }, [props.isActive]); + + useEffect(() => { + if (props.isActive === previousIsActive) { + return; + } + + animateFloatingActionButton(); + }, [props.isActive, animateFloatingActionButton, previousIsActive]); + + const rotate = animatedValue.current.interpolate({ + inputRange: [0, 1], + outputRange: ['0deg', '135deg'], + }); + + const backgroundColor = animatedValue.current.interpolate({ + inputRange: [0, 1], + outputRange: [props.theme.success, props.theme.buttonDefaultBG], + }); + + const fill = animatedValue.current.interpolate({ + inputRange: [0, 1], + outputRange: [props.theme.textLight, props.theme.textDark], + }); + + return ( + + + { + fabPressable.current = el; + if (props.buttonRef) { + // eslint-disable-next-line no-param-reassign + props.buttonRef.current = el; + } + }} + accessibilityLabel={props.accessibilityLabel} + role={props.role} + pressDimmingValue={1} + onPress={(e) => { + // Drop focus to avoid blue focus ring. + fabPressable.current.blur(); + props.onPress(e); + }} + onLongPress={() => {}} + style={[props.themeStyles.floatingActionButton, StyleUtils.getAnimatedFABStyle(rotate, backgroundColor)]} + > + + + + + ); } FloatingActionButton.propTypes = propTypes; @@ -128,4 +126,4 @@ const FloatingActionButtonWithLocalizeWithRef = React.forwardRef((props, ref) => FloatingActionButtonWithLocalizeWithRef.displayName = 'FloatingActionButtonWithLocalizeWithRef'; -export default compose(withThemeStyles, withTheme)(FloatingActionButtonWithLocalizeWithRef); +export default compose(withThemeStyles, withTheme, memo)(FloatingActionButtonWithLocalizeWithRef);