Skip to content

[core] Remove RootRef usage #15347

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

Merged
merged 7 commits into from
Apr 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/material-ui/src/Dialog/Dialog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('<Dialog />', () => {
);

it('should render a Modal with TransitionComponent', () => {
const Transition = () => <div tabIndex={-1} />;
const Transition = React.forwardRef(() => <div tabIndex={-1} />);
const wrapper = mount(
<Dialog {...defaultProps} open TransitionComponent={Transition}>
foo
Expand Down
7 changes: 5 additions & 2 deletions packages/material-ui/src/Fade/Fade.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Transition } from 'react-transition-group';
import { duration } from '../styles/transitions';
import withTheme from '../styles/withTheme';
import { reflow, getTransitionProps } from '../transitions/utils';
import { useForkRef } from '../utils/reactHelpers';

const styles = {
entering: {
Expand All @@ -20,8 +21,9 @@ const styles = {
* The Fade transition is used by the [Modal](/utils/modal/) component.
* It uses [react-transition-group](https://github.com/reactjs/react-transition-group) internally.
*/
function Fade(props) {
const Fade = React.forwardRef(function Fade(props, ref) {
const { children, in: inProp, onEnter, onExit, style, theme, ...other } = props;
const handleRef = useForkRef(children.ref, ref);

const handleEnter = node => {
reflow(node); // So the animation always start from the start.
Expand Down Expand Up @@ -60,12 +62,13 @@ function Fade(props) {
...style,
...children.props.style,
},
ref: handleRef,
...childProps,
});
}}
</Transition>
);
}
});

Fade.propTypes = {
/**
Expand Down
7 changes: 5 additions & 2 deletions packages/material-ui/src/Grow/Grow.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import { Transition } from 'react-transition-group';
import withTheme from '../styles/withTheme';
import { reflow, getTransitionProps } from '../transitions/utils';
import { useForkRef } from '../utils/reactHelpers';

function getScale(value) {
return `scale(${value}, ${value ** 2})`;
Expand All @@ -27,10 +28,11 @@ const styles = {
* [Popover](/utils/popover/) components.
* It uses [react-transition-group](https://github.com/reactjs/react-transition-group) internally.
*/
function Grow(props) {
const Grow = React.forwardRef(function Grow(props, ref) {
const { children, in: inProp, onEnter, onExit, style, theme, timeout, ...other } = props;
const timer = React.useRef();
const autoTimeout = React.useRef();
const handleRef = useForkRef(children.ref, ref);

const handleEnter = node => {
reflow(node); // So the animation always start from the start.
Expand Down Expand Up @@ -126,12 +128,13 @@ function Grow(props) {
...style,
...children.props.style,
},
ref: handleRef,
...childProps,
});
}}
</Transition>
);
}
});

Grow.propTypes = {
/**
Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui/src/Modal/Modal.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ describe('<Modal />', () => {
});

it('should warn if the modal content is not focusable', () => {
const Dialog = () => <div />;
const Dialog = React.forwardRef((_, ref) => <div ref={ref} />);

wrapper = mount(
<Modal open>
Expand Down
15 changes: 11 additions & 4 deletions packages/material-ui/src/Modal/TrapFocus.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
/* eslint-disable consistent-return, jsx-a11y/no-noninteractive-tabindex */

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import warning from 'warning';
import RootRef from '../RootRef';
import ownerDocument from '../utils/ownerDocument';
import { useForkRef } from '../utils/reactHelpers';

function TrapFocus(props) {
const {
children,
disableAutoFocus,
disableEnforceFocus,
disableRestoreFocus,
getDoc,
isEnabled,
open,
} = props;
const rootRef = React.useRef();
const ignoreNextEnforceFocus = React.useRef();
const sentinelStart = React.useRef();
const sentinelEnd = React.useRef();
const lastFocus = React.useRef();
const rootRef = React.useRef();
// can be removed once we drop support for non ref forwarding class components
const handleOwnRef = React.useCallback(ref => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Rather use instance.

Suggested change
const handleOwnRef = React.useCallback(ref => {
const handleOwnRef = React.useCallback(instance => {

A ref would be the the pointer-type i.e.

ref = { current: instance }
      ^^^^^^^^^^^^^^^^^^^^^ the ref as a pointer-like object
                 ^^^^^^^^ the actual instance
ref = instance => {}
      ^^^^^^^^^^^^^^ the ref as a callback that is fired every time the instance changes
      ^^^^^^^^ the instance

rootRef.current = ReactDOM.findDOMNode(ref);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can add the // StrictMode ready comment. If @eps1lon accepts it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Actually every findDOMNode that get's an instance from a ref should be strict-mode.

}, []);
const handleRef = useForkRef(children.ref, handleOwnRef);

// ⚠️ You may rely on React.useMemo as a performance optimization, not as a semantic guarantee.
// https://reactjs.org/docs/hooks-reference.html#usememo
Expand Down Expand Up @@ -61,7 +68,7 @@ function TrapFocus(props) {
return;
}

if (!rootRef.current.contains(doc.activeElement)) {
if (rootRef.current && !rootRef.current.contains(doc.activeElement)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we warn if rootRef.current is not an instance of HTMLElement but the TrapFocus is enabled (like we do in the Popper's prop-types)? It's a broad question, it's no tight to this effort.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Probably better than checking for focus i.e. duck-typing.

rootRef.current.focus();
}
};
Expand Down Expand Up @@ -109,7 +116,7 @@ function TrapFocus(props) {
return (
<React.Fragment>
<div tabIndex={0} ref={sentinelStart} data-test="sentinelStart" />
<RootRef rootRef={rootRef}>{props.children}</RootRef>
{React.cloneElement(children, { ref: handleRef })}
<div tabIndex={0} ref={sentinelEnd} data-test="sentinelEnd" />
</React.Fragment>
);
Expand Down