Skip to content

Commit c54e545

Browse files
authored
[Popper] Refactor to more commonly known react patterns (#16613)
* [Popper] Fix re-creation when placement flips * Apply controlled/uncontrolled/derived state pattern
1 parent 6dc4ca3 commit c54e545

File tree

1 file changed

+19
-11
lines changed

1 file changed

+19
-11
lines changed

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

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import Portal from '../Portal';
66
import { createChainedFunction } from '../utils/helpers';
77
import { useForkRef } from '../utils/reactHelpers';
88

9+
/**
10+
* Flips placement if in <body dir="rtl" />
11+
* @param {string} placement
12+
*/
913
function flipPlacement(placement) {
1014
const direction = (typeof window !== 'undefined' && document.body.getAttribute('dir')) || 'ltr';
1115

@@ -47,7 +51,7 @@ const Popper = React.forwardRef(function Popper(props, ref) {
4751
keepMounted = false,
4852
modifiers,
4953
open,
50-
placement: placementProps = 'bottom',
54+
placement: initialPlacement = 'bottom',
5155
popperOptions = defaultPopperOptions,
5256
popperRef: popperRefProp,
5357
transition = false,
@@ -65,13 +69,20 @@ const Popper = React.forwardRef(function Popper(props, ref) {
6569
React.useImperativeHandle(popperRefProp, () => popperRef.current, []);
6670

6771
const [exited, setExited] = React.useState(!open);
68-
const [placement, setPlacement] = React.useState();
72+
73+
const rtlPlacement = flipPlacement(initialPlacement);
74+
/**
75+
* placement initialized from prop but can change during lifetime if modifiers.flip.
76+
* modifiers.flip is essentially a flip for controlled/uncontrolled behavior
77+
*/
78+
const [placement, setPlacement] = React.useState(rtlPlacement);
79+
if (rtlPlacement !== placement) {
80+
setPlacement(rtlPlacement);
81+
}
6982

7083
const handleOpen = React.useCallback(() => {
7184
const handlePopperUpdate = data => {
72-
if (data.placement !== placement) {
73-
setPlacement(data.placement);
74-
}
85+
setPlacement(data.placement);
7586
};
7687

7788
const popperNode = tooltipRef.current;
@@ -86,7 +97,7 @@ const Popper = React.forwardRef(function Popper(props, ref) {
8697
}
8798

8899
const popper = new PopperJS(getAnchorEl(anchorEl), popperNode, {
89-
placement: flipPlacement(placementProps),
100+
placement: rtlPlacement,
90101
...popperOptions,
91102
modifiers: {
92103
...(disablePortal
@@ -102,11 +113,10 @@ const Popper = React.forwardRef(function Popper(props, ref) {
102113
},
103114
// We could have been using a custom modifier like react-popper is doing.
104115
// But it seems this is the best public API for this use case.
105-
onCreate: createChainedFunction(handlePopperUpdate, popperOptions.onCreate),
106116
onUpdate: createChainedFunction(handlePopperUpdate, popperOptions.onUpdate),
107117
});
108118
handlePopperRefRef.current(popper);
109-
}, [anchorEl, disablePortal, modifiers, open, placement, placementProps, popperOptions]);
119+
}, [anchorEl, disablePortal, modifiers, open, rtlPlacement, popperOptions]);
110120

111121
const handleEnter = () => {
112122
setExited(false);
@@ -148,9 +158,7 @@ const Popper = React.forwardRef(function Popper(props, ref) {
148158
return null;
149159
}
150160

151-
const childProps = {
152-
placement: placement || flipPlacement(placementProps),
153-
};
161+
const childProps = { placement };
154162

155163
if (transition) {
156164
childProps.TransitionProps = {

0 commit comments

Comments
 (0)