Reputation: 313
I encountered an issue when adding material UI to my already existing application. Basically what's happening is that when I add material UI components to my modals, the entering animation of the modal does not trigger. Downgrading material UI to 1.0.0 or removing all MUI components solves the issue. Also, using any other UI library does not cause this issue.
https://codesandbox.io/s/mui-animation-issue-mgph3
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import Test from "./Test";
const Overlay = styled.div`
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 10000;
`;
const Modal = styled.div`
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
width: 100px;
height: 100px;
align-items: center;
background: white;
`;
const modalsComponentLookupTable = {
Test
};
const ModalContainer = ({ setModals, children }) => {
const [modalStyle, setModalStyle] = useState({
opacity: 0,
transition: "opacity 2000ms",
willChange: "opacity",
transitionTimingFunction: "cubic-bezier(0.165, 0.840, 0.440, 1.000)"
});
const [bgStyle, setBgStyle] = useState({
background: "rgba(0, 0, 0, 1)",
willChange: "opacity",
transition: "opacity 2000ms cubic-bezier(0.165, 0.840, 0.440, 1.000)",
opacity: 0
});
const unMountStyle = () => {
// css for unmount animation
setModalStyle({
opacity: 0,
transition: "opacity 2200ms",
willChange: "opacity",
transitionTimingFunction: "cubic-bezier(0.165, 0.840, 0.440, 1.000)"
});
setBgStyle({
background: "rgba(0, 0, 0, 1)",
willChange: "opacity",
transition: "opacity 2200ms cubic-bezier(0.165, 0.840, 0.440, 1.000)",
opacity: 0
});
};
const mountStyle = () => {
// css for mount animation
setModalStyle({
opacity: 1,
transition: "opacity: 2000ms",
willChange: "opacity",
transitionTimingFunction: "cubic-bezier(0.165, 0.840, 0.440, 1.000)"
});
setBgStyle({
willChange: "opacity",
opacity: 1,
background: "rgba(0, 0, 0, 1)",
transition: "opacity 2000ms cubic-bezier(0.165, 0.840, 0.440, 1.000)"
});
};
useEffect(() => {
mountStyle();
}, []);
const back = e => {
e.stopPropagation();
unMountStyle();
setTimeout(() => setModals([]), 2200);
};
return (
<Overlay onClick={back} style={bgStyle}>
<Modal style={modalStyle}>{children}</Modal>
</Overlay>
);
};
const ModalsManager = ({ modals, setModals }) => {
const renderedModals = modals.map(modalDescription => {
const ModalComponent = modalsComponentLookupTable[modalDescription];
return (
<ModalContainer setModals={setModals}>
<ModalComponent />
</ModalContainer>
);
});
return <span>{renderedModals}</span>;
};
export default ModalsManager;
When the Test
component contains any kind of MUI components, the entering animation doesn't trigger. No error in the console.
Apparently it's something in my code that does trigger this issue since I already opened an issue on their github and they said it's not an issue with their library: https://github.com/mui-org/material-ui/issues/17888
Upvotes: 2
Views: 8102
Reputation: 80986
I have isolated this sufficiently to be convinced that it is not a problem with Material-UI, but rather a brittleness in the approach used for your transition animation.
All I have to do to break it is to include a component that immediately re-renders on mount (either in componentDidMount
or useLayoutEffect
). This is something that TransitionGroup does and TransitionGroup
is used by TouchRipple which is used by ButtonBase
which is used by several Material-UI components such as button.
Changing your Test
component to the following was sufficient to cause the bad behavior:
import React from "react";
const RerenderOnMount = () => {
const [, setMyState] = React.useState(false);
React.useLayoutEffect(() => {
setMyState(true);
}, []);
return null;
};
const Test = () => {
return (
<div
css={`
height: 100px;
width: 100px;
z-index: 5;
`}
onClick={e => e.stopPropagation()}
>
<div>
Test
<RerenderOnMount />
</div>
</div>
);
};
export default Test;
In the example above which still reproduces your problem, no Material-UI components are being used.
I believe the re-render of the child element is impacting (delaying) the timing of when the browser first tries to paint the modal and is causing the browser to not recognize that a transition has occurred when you call mountStyle
(i.e. behaving the same as if you had used the "mount" styles initially rather than recognizing a transition from the default styles to the mount styles). The trickiness in getting this timing correct in React to ensure the browser does a transition is the reason why people typically use react-transition-group to help with this (as Material-UI does).
I was able to get your code to work by calling mountStyle
via setTimeout
within the useEffect
, but I can't guarantee that this hack won't have issues in other cases (depending on what is in the modal) or different browsers, and I would instead recommend reworking the transition to let react-transition-group
manage the entering/exiting states. Here's the working version with my setTimeout
hack: https://codesandbox.io/s/mui-animation-issue-505rj.
Upvotes: 5