react-beautiful-dnd-next
Version:
Beautiful and accessible drag and drop for lists with React
96 lines (81 loc) • 2.07 kB
JSX
// @flow
import React, { type Node } from 'react';
import type { InOutAnimationMode } from '../../types';
export type AnimateProvided = {|
onClose: () => void,
animate: InOutAnimationMode,
data: mixed,
|};
type Props = {|
on: mixed,
shouldAnimate: boolean,
children: (provided: AnimateProvided) => Node,
|};
type State = {|
data: mixed,
isVisible: boolean,
animate: InOutAnimationMode,
|};
// Using a class here rather than hooks because
// getDerivedStateFromProps results in far less renders.
// Using hooks to implement this was quite messy and resulted in lots of additional renders
export default class AnimateInOut extends React.PureComponent<Props, State> {
state: State = {
isVisible: Boolean(this.props.on),
data: this.props.on,
// not allowing to animate close on mount
animate: this.props.shouldAnimate && this.props.on ? 'open' : 'none',
};
static getDerivedStateFromProps(props: Props, state: State): State {
if (!props.shouldAnimate) {
return {
isVisible: Boolean(props.on),
data: props.on,
animate: 'none',
};
}
// need to animate in
if (props.on) {
return {
isVisible: true,
// have new data to animate in with
data: props.on,
animate: 'open',
};
}
// need to animate out if there was data
if (state.isVisible) {
return {
isVisible: true,
// use old data for animating out
data: state.data,
animate: 'close',
};
}
// close animation no longer visible
return {
isVisible: false,
animate: 'close',
data: null,
};
}
onClose = () => {
if (this.state.animate !== 'close') {
return;
}
this.setState({
isVisible: false,
});
};
render() {
if (!this.state.isVisible) {
return null;
}
const provided: AnimateProvided = {
onClose: this.onClose,
data: this.state.data,
animate: this.state.animate,
};
return this.props.children(provided);
}
}