react-native-animatable-unmountable
Version:
Declarative transitions and animations for React Native (with unmount effect)
129 lines (106 loc) • 3.41 kB
JavaScript
/**
* HOC that enhances react-native-animatable with exit (unmount) animations
*
* Possible states: prop.mounted : state.mounted
*
* - true : true - mounted (mounted)
* - false : true - intermediate (unmounting/exiting)
* - false : false - hidden (unmounted)
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { createAnimatableComponent } from 'react-native-animatable';
export default function animatableUnmountable(WrappedComponent) {
const AnimatableComponent = createAnimatableComponent(WrappedComponent);
return class extends Component {
static propTypes = {
didMount: PropTypes.func,
didUnmount: PropTypes.func,
innerRef: PropTypes.func,
mounted: PropTypes.bool,
onUnmountAnimationBegin: PropTypes.func,
onUnmountAnimationEnd: PropTypes.func,
unmountAnimation: PropTypes.string,
unmountDuration: PropTypes.number,
};
static defaultProps = {
didMount: () => {},
didUnmount: () => {},
innerRef: ref => {},
mounted: true,
onUnmountAnimationBegin: () => {},
onUnmountAnimationEnd: endState => {},
unmountAnimation: null,
unmountDuration: 1000,
};
container = null;
state = {
mounted: this.props.mounted,
};
componentDidUpdate(prevProps, prevState) {
if (!prevProps.mounted && this.props.mounted) {
this.doMount();
}
if (prevProps.mounted && !this.props.mounted) {
this.doUnmount();
}
if (prevState.mounted && !this.state.mounted) {
this.props.didUnmount();
}
}
onAnimationEnd = endState => {
this.props.onAnimationEnd && this.props.onAnimationEnd(endState);
this.props.didMount();
};
doMount = () => {
this.container && this.container.stopAnimation();
this.setState({ mounted: true });
};
doUnmount = () => {
this.container && this.container.stopAnimation();
if (!this.props.unmountAnimation) {
if (!this.props.mounted) {
this.setState({ mounted: false });
}
return;
}
this.props.onUnmountAnimationBegin();
// fallback if container not found
if (!this.container) {
if (!this.props.mounted) {
this.setState({ mounted: false });
this.props.onUnmountAnimationEnd({ finished: true });
}
return;
}
const unmountDuration = this.props.unmountDuration || this.props.duration * 0.75;
const animation = typeof this.props.unmountAnimation === 'string'
? this.container[this.props.unmountAnimation](unmountDuration)
: this.container.animate(this.props.unmountAnimation, unmountDuration)
animation.then(endState => {
// don't change if changed to mounted in the meantime
// cannot be: prop.mounted=true, state.mounted=false
if (!this.props.mounted) {
this.setState({ mounted: false });
this.props.onUnmountAnimationEnd(endState);
}
});
};
handleRef = ref => {
this.container = ref;
this.props.innerRef(ref);
};
render() {
if (!this.state.mounted) {
return null;
}
return (
<AnimatableComponent
ref={this.handleRef}
{...this.props}
onAnimationEnd={this.onAnimationEnd}
/>
);
}
};
};