Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Slide] Convert to function component #15344

Merged
merged 12 commits into from
Apr 19, 2019
225 changes: 108 additions & 117 deletions packages/material-ui/src/Slide/Slide.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import EventListener from 'react-event-listener';
import debounce from 'debounce'; // < 1kb payload overhead when lodash/debounce is > 3kb.
import Transition from 'react-transition-group/Transition';
import ownerWindow from '../utils/ownerWindow';
import { setRef } from '../utils/reactHelpers';
import { useForkRef } from '../utils/reactHelpers';
import withTheme from '../styles/withTheme';
import { duration } from '../styles/transitions';
import { reflow, getTransitionProps } from '../transitions/utils';
Expand Down Expand Up @@ -73,63 +72,43 @@ export function setTranslateValue(props, node) {
* The Slide transition is used by the [Drawer](/demos/drawers/) component.
* It uses [react-transition-group](https://github.com/reactjs/react-transition-group) internally.
*/
class Slide extends React.Component {
mounted = false;

constructor() {
super();

if (typeof window !== 'undefined') {
this.handleResize = debounce(() => {
// Skip configuration where the position is screen size invariant.
if (this.props.in || this.props.direction === 'down' || this.props.direction === 'right') {
return;
}

if (this.childDOMNode) {
setTranslateValue(this.props, this.childDOMNode);
}
}, 166); // Corresponds to 10 frames at 60 Hz.
}
}

componentDidMount() {
this.mounted = true;

// state.mounted handle SSR, once the component is mounted, we need
// to properly hide it.
if (!this.props.in) {
// We need to set initial translate values of transition element
// otherwise component will be shown when in=false.
this.updatePosition();
}
}

componentDidUpdate(prevProps) {
if (prevProps.direction !== this.props.direction && !this.props.in) {
// We need to update the position of the drawer when the direction change and
// when it's hidden.
this.updatePosition();
}
}

componentWillUnmount() {
this.handleResize.clear();
}
function Slide(props) {
const {
children,
direction,
in: inProp,
onEnter,
onEntering,
onExit,
onExited,
style,
theme,
...other
} = props;

const mountedRef = React.useRef(false);
const childrenRef = React.useRef();
const [prevDirection, setPrevDirection] = React.useState();
/**
* used in cloneElement(children, { ref: handleRef })
*/
const handleOwnRef = ref => {
// #StrictMode ready
childrenRef.current = ReactDOM.findDOMNode(ref);
};
const handleRef = useForkRef(children.ref, handleOwnRef);

handleEnter = node => {
setTranslateValue(this.props, node);
const handleEnter = node => {
joshwooding marked this conversation as resolved.
Show resolved Hide resolved
setTranslateValue(props, node);
reflow(node);

if (this.props.onEnter) {
this.props.onEnter(node);
if (onEnter) {
onEnter(node);
}
};

handleEntering = node => {
const { theme } = this.props;

const transitionProps = getTransitionProps(this.props, {
const handleEntering = node => {
const transitionProps = getTransitionProps(props, {
mode: 'enter',
});
node.style.webkitTransition = theme.transitions.create('-webkit-transform', {
Expand All @@ -142,15 +121,13 @@ class Slide extends React.Component {
});
node.style.webkitTransform = 'translate(0, 0)';
node.style.transform = 'translate(0, 0)';
if (this.props.onEntering) {
this.props.onEntering(node);
if (onEntering) {
onEntering(node);
}
};

handleExit = node => {
const { theme } = this.props;

const transitionProps = getTransitionProps(this.props, {
const handleExit = node => {
const transitionProps = getTransitionProps(props, {
mode: 'exit',
});
node.style.webkitTransition = theme.transitions.create('-webkit-transform', {
Expand All @@ -161,78 +138,92 @@ class Slide extends React.Component {
...transitionProps,
easing: theme.transitions.easing.sharp,
});
setTranslateValue(this.props, node);
setTranslateValue(props, node);

if (this.props.onExit) {
this.props.onExit(node);
if (onExit) {
onExit(node);
}
};

handleExited = node => {
const handleExited = node => {
// No need for transitions when the component is hidden
node.style.webkitTransition = '';
node.style.transition = '';

if (this.props.onExited) {
this.props.onExited(node);
if (onExited) {
onExited(node);
}
};

/**
* used in cloneElement(children, { ref: handleRef })
*/
handleRef = ref => {
// #StrictMode ready
this.childDOMNode = ReactDOM.findDOMNode(ref);
setRef(this.props.children.ref, ref);
};
const updatePosition = React.useCallback(() => {
if (childrenRef.current) {
setTranslateValue(props, childrenRef.current);
}
}, [props]);
joshwooding marked this conversation as resolved.
Show resolved Hide resolved

React.useEffect(() => {
const handleResize = debounce(() => {
// Skip configuration where the position is screen size invariant.
if (inProp || direction === 'down' || direction === 'right') {
joshwooding marked this conversation as resolved.
Show resolved Hide resolved
return;
}

if (childrenRef.current) {
setTranslateValue(props, childrenRef.current);
}
}, 166); // Corresponds to 10 frames at 60 Hz.

window.addEventListener('resize', handleResize);

updatePosition() {
if (this.childDOMNode) {
setTranslateValue(this.props, this.childDOMNode);
return () => {
handleResize.clear();
window.removeEventListener('resize', handleResize);
};
}, [direction, inProp, props]);

React.useEffect(() => {
// state.mounted handle SSR, once the component is mounted, we need
// to properly hide it.
if (!inProp && !mountedRef.current) {
joshwooding marked this conversation as resolved.
Show resolved Hide resolved
// We need to set initial translate values of transition element
// otherwise component will be shown when in=false.
updatePosition();
}
}
mountedRef.current = true;
}, [inProp, updatePosition]);

render() {
const {
children,
direction,
in: inProp,
onEnter,
onEntering,
onExit,
onExited,
style,
theme,
...other
} = this.props;

return (
<EventListener target="window" onResize={this.handleResize}>
<Transition
onEnter={this.handleEnter}
onEntering={this.handleEntering}
onExit={this.handleExit}
onExited={this.handleExited}
appear
in={inProp}
{...other}
>
{(state, childProps) => {
return React.cloneElement(children, {
ref: this.handleRef,
style: {
visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
...style,
...children.props.style,
},
...childProps,
});
}}
</Transition>
</EventListener>
);
}
React.useEffect(() => {
if (prevDirection !== direction && !inProp) {
// We need to update the position of the drawer when the direction change and
// when it's hidden.
updatePosition();
}
setPrevDirection(direction);
}, [direction, inProp, prevDirection, updatePosition]);
joshwooding marked this conversation as resolved.
Show resolved Hide resolved

return (
<Transition
onEnter={handleEnter}
onEntering={handleEntering}
onExit={handleExit}
onExited={handleExited}
appear
in={inProp}
{...other}
>
{(state, childProps) => {
return React.cloneElement(children, {
ref: handleRef,
joshwooding marked this conversation as resolved.
Show resolved Hide resolved
style: {
visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
...style,
...children.props.style,
},
...childProps,
});
}}
</Transition>
);
}

Slide.propTypes = {
Expand Down
Loading