Skip to content

Commit 19d418d

Browse files
joshwoodingoliviertassinari
authored andcommitted
[Slide] Convert to function component (#15344)
* [Slide] Convert to function component * Remove innerRef propType from Table * Handle change in style formatting in tests * Code review * Code review v2 * Fix tests * Add useCallback to handleOwnRef * Prettier * Add breaking change to migration docs
1 parent ccc466a commit 19d418d

File tree

4 files changed

+335
-255
lines changed

4 files changed

+335
-255
lines changed

docs/src/pages/guides/migration-v3/migration-v3.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,26 @@ You should be able to move the custom styles to the root class key.
341341
Only special HTML elements have these default behaviors.
342342
People should use `event.stopPropagation()` if they don't want to trigger a `onClose` event on the modal.
343343

344+
### Slide
345+
346+
- [Slide] The child needs to be able to hold a ref.
347+
348+
```diff
349+
class Component extends React.Component {
350+
render() {
351+
return <div />
352+
}
353+
}
354+
-const MyComponent = props => <div {...props} />
355+
+const MyComponent = React.forwardRef((props, ref) => <div ref={ref} {...props} />);
356+
<Slide><Component /></Slide>
357+
<Slide><MyComponent /></Slide>
358+
<Slide><div /></Slide>
359+
344360
### Tooltip
345361

346362
- [Tooltip] The child needs to be able to hold a ref.
347-
363+
348364
```diff
349365
class Component extends React.Component {
350366
render() {

packages/material-ui/src/Slide/Slide.js

Lines changed: 113 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
import React from 'react';
44
import PropTypes from 'prop-types';
55
import ReactDOM from 'react-dom';
6-
import EventListener from 'react-event-listener';
76
import debounce from 'debounce'; // < 1kb payload overhead when lodash/debounce is > 3kb.
87
import { Transition } from 'react-transition-group';
9-
import { setRef } from '../utils/reactHelpers';
8+
import { useForkRef } from '../utils/reactHelpers';
109
import withTheme from '../styles/withTheme';
1110
import { duration } from '../styles/transitions';
1211
import { reflow, getTransitionProps } from '../transitions/utils';
@@ -16,8 +15,7 @@ const GUTTER = 24;
1615
// Translate the node so he can't be seen on the screen.
1716
// Later, we gonna translate back the node to his original location
1817
// with `translate3d(0, 0, 0)`.`
19-
function getTranslateValue(props, node) {
20-
const { direction } = props;
18+
function getTranslateValue(direction, node) {
2119
const rect = node.getBoundingClientRect();
2220

2321
let transform;
@@ -59,8 +57,8 @@ function getTranslateValue(props, node) {
5957
return `translateY(-${rect.top + rect.height + GUTTER - offsetY}px)`;
6058
}
6159

62-
export function setTranslateValue(props, node) {
63-
const transform = getTranslateValue(props, node);
60+
export function setTranslateValue(direction, node) {
61+
const transform = getTranslateValue(direction, node);
6462

6563
if (transform) {
6664
node.style.webkitTransform = transform;
@@ -72,65 +70,49 @@ export function setTranslateValue(props, node) {
7270
* The Slide transition is used by the [Drawer](/demos/drawers/) component.
7371
* It uses [react-transition-group](https://github.com/reactjs/react-transition-group) internally.
7472
*/
75-
class Slide extends React.Component {
76-
mounted = false;
77-
78-
constructor() {
79-
super();
80-
81-
if (typeof window !== 'undefined') {
82-
this.handleResize = debounce(() => {
83-
// Skip configuration where the position is screen size invariant.
84-
if (this.props.in || this.props.direction === 'down' || this.props.direction === 'right') {
85-
return;
86-
}
87-
88-
if (this.childDOMNode) {
89-
setTranslateValue(this.props, this.childDOMNode);
90-
}
91-
}, 166); // Corresponds to 10 frames at 60 Hz.
92-
}
93-
}
94-
95-
componentDidMount() {
96-
this.mounted = true;
97-
98-
// state.mounted handle SSR, once the component is mounted, we need
99-
// to properly hide it.
100-
if (!this.props.in) {
101-
// We need to set initial translate values of transition element
102-
// otherwise component will be shown when in=false.
103-
this.updatePosition();
104-
}
105-
}
106-
107-
componentDidUpdate(prevProps) {
108-
if (prevProps.direction !== this.props.direction && !this.props.in) {
109-
// We need to update the position of the drawer when the direction change and
110-
// when it's hidden.
111-
this.updatePosition();
112-
}
113-
}
114-
115-
componentWillUnmount() {
116-
this.handleResize.clear();
117-
}
73+
function Slide(props) {
74+
const {
75+
children,
76+
direction,
77+
in: inProp,
78+
onEnter,
79+
onEntering,
80+
onExit,
81+
onExited,
82+
style,
83+
theme,
84+
timeout,
85+
...other
86+
} = props;
87+
88+
const childrenRef = React.useRef();
89+
/**
90+
* used in cloneElement(children, { ref: handleRef })
91+
*/
92+
const handleOwnRef = React.useCallback(ref => {
93+
// #StrictMode ready
94+
childrenRef.current = ReactDOM.findDOMNode(ref);
95+
}, []);
96+
const handleRef = useForkRef(children.ref, handleOwnRef);
11897

119-
handleEnter = node => {
120-
setTranslateValue(this.props, node);
98+
const handleEnter = () => {
99+
const node = childrenRef.current;
100+
setTranslateValue(direction, node);
121101
reflow(node);
122102

123-
if (this.props.onEnter) {
124-
this.props.onEnter(node);
103+
if (onEnter) {
104+
onEnter(node);
125105
}
126106
};
127107

128-
handleEntering = node => {
129-
const { theme } = this.props;
130-
131-
const transitionProps = getTransitionProps(this.props, {
132-
mode: 'enter',
133-
});
108+
const handleEntering = () => {
109+
const node = childrenRef.current;
110+
const transitionProps = getTransitionProps(
111+
{ timeout, style },
112+
{
113+
mode: 'enter',
114+
},
115+
);
134116
node.style.webkitTransition = theme.transitions.create('-webkit-transform', {
135117
...transitionProps,
136118
easing: theme.transitions.easing.easeOut,
@@ -141,17 +123,19 @@ class Slide extends React.Component {
141123
});
142124
node.style.webkitTransform = 'translate(0, 0)';
143125
node.style.transform = 'translate(0, 0)';
144-
if (this.props.onEntering) {
145-
this.props.onEntering(node);
126+
if (onEntering) {
127+
onEntering(node);
146128
}
147129
};
148130

149-
handleExit = node => {
150-
const { theme } = this.props;
151-
152-
const transitionProps = getTransitionProps(this.props, {
153-
mode: 'exit',
154-
});
131+
const handleExit = () => {
132+
const node = childrenRef.current;
133+
const transitionProps = getTransitionProps(
134+
{ timeout, style },
135+
{
136+
mode: 'exit',
137+
},
138+
);
155139
node.style.webkitTransition = theme.transitions.create('-webkit-transform', {
156140
...transitionProps,
157141
easing: theme.transitions.easing.sharp,
@@ -160,78 +144,82 @@ class Slide extends React.Component {
160144
...transitionProps,
161145
easing: theme.transitions.easing.sharp,
162146
});
163-
setTranslateValue(this.props, node);
147+
setTranslateValue(direction, node);
164148

165-
if (this.props.onExit) {
166-
this.props.onExit(node);
149+
if (onExit) {
150+
onExit(node);
167151
}
168152
};
169153

170-
handleExited = node => {
154+
const handleExited = () => {
155+
const node = childrenRef.current;
171156
// No need for transitions when the component is hidden
172157
node.style.webkitTransition = '';
173158
node.style.transition = '';
174159

175-
if (this.props.onExited) {
176-
this.props.onExited(node);
160+
if (onExited) {
161+
onExited(node);
177162
}
178163
};
179164

180-
/**
181-
* used in cloneElement(children, { ref: handleRef })
182-
*/
183-
handleRef = ref => {
184-
// #StrictMode ready
185-
this.childDOMNode = ReactDOM.findDOMNode(ref);
186-
setRef(this.props.children.ref, ref);
187-
};
165+
const updatePosition = React.useCallback(() => {
166+
if (childrenRef.current) {
167+
setTranslateValue(direction, childrenRef.current);
168+
}
169+
}, [direction]);
170+
171+
React.useEffect(() => {
172+
// Skip configuration where the position is screen size invariant.
173+
if (!inProp && direction !== 'down' && direction !== 'right') {
174+
const handleResize = debounce(() => {
175+
if (childrenRef.current) {
176+
setTranslateValue(direction, childrenRef.current);
177+
}
178+
}, 166); // Corresponds to 10 frames at 60 Hz.
188179

189-
updatePosition() {
190-
if (this.childDOMNode) {
191-
setTranslateValue(this.props, this.childDOMNode);
180+
window.addEventListener('resize', handleResize);
181+
182+
return () => {
183+
handleResize.clear();
184+
window.removeEventListener('resize', handleResize);
185+
};
192186
}
193-
}
194187

195-
render() {
196-
const {
197-
children,
198-
direction,
199-
in: inProp,
200-
onEnter,
201-
onEntering,
202-
onExit,
203-
onExited,
204-
style,
205-
theme,
206-
...other
207-
} = this.props;
208-
209-
return (
210-
<EventListener target="window" onResize={this.handleResize}>
211-
<Transition
212-
onEnter={this.handleEnter}
213-
onEntering={this.handleEntering}
214-
onExit={this.handleExit}
215-
onExited={this.handleExited}
216-
appear
217-
in={inProp}
218-
{...other}
219-
>
220-
{(state, childProps) => {
221-
return React.cloneElement(children, {
222-
ref: this.handleRef,
223-
style: {
224-
visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
225-
...style,
226-
...children.props.style,
227-
},
228-
...childProps,
229-
});
230-
}}
231-
</Transition>
232-
</EventListener>
233-
);
234-
}
188+
return () => {};
189+
}, [direction, inProp]);
190+
191+
React.useEffect(() => {
192+
if (!inProp) {
193+
// We need to update the position of the drawer when the direction change and
194+
// when it's hidden.
195+
updatePosition();
196+
}
197+
}, [inProp, updatePosition]);
198+
199+
return (
200+
<Transition
201+
onEnter={handleEnter}
202+
onEntering={handleEntering}
203+
onExit={handleExit}
204+
onExited={handleExited}
205+
appear
206+
in={inProp}
207+
timeout={timeout}
208+
{...other}
209+
>
210+
{(state, childProps) => {
211+
return React.cloneElement(children, {
212+
ref: handleRef,
213+
style: {
214+
visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
215+
...style,
216+
...children.props.style,
217+
},
218+
...childProps,
219+
});
220+
}}
221+
</Transition>
222+
);
235223
}
236224

237225
Slide.propTypes = {

0 commit comments

Comments
 (0)