Material UI causing animation to fail to trigger

After integrating material UI into my existing application, I encountered a peculiar issue. When adding material UI components to modals, the entering animation fails to trigger. Interestingly, downgrading material UI or removing all MUI components resolves the problem, and using any other UI library does not reproduce the 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 utilizing MUI components within the Test component, the entering animation does not activate. No error messages appear in the console. It seems that there might be an issue within my code causing this behavior, as the Material-UI team mentioned it's not related to their library according to their response on GitHub: https://github.com/mui-org/material-ui/issues/17888

Answer №1

After thorough investigation, I am confident that the issue does not lie within Material-UI but rather in the fragility of the approach utilized for your transition animation.

I discovered that simply including a component that immediately re-renders upon mounting (whether in componentDidMount or useLayoutEffect) is enough to disrupt the functionality. This behavior is exhibited by TransitionGroup, which is employed by TouchRipple utilized by ButtonBase, and subsequently used in various Material-UI components like buttons.

The alteration made to your Test component below was responsible for triggering the faulty 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 that replicates the issue, no Material-UI components are involved.

It appears that the re-rendering of the child component impacts the timing at which the browser attempts to render the modal initially, leading the browser to overlook any transitions when mountStyle is called. This results in the same output as if the "mount" styles were applied directly, without acknowledging the transition from default styles to mount styles. To address this timing intricacy in React and ensure proper transitions, it is advisable to utilize react-transition-group as commonly done in Material-UI.

To temporarily resolve the issue, I managed to make your code work by invoking mountStyle using setTimeout within useEffect. However, this workaround may pose potential issues in different scenarios or browsers. Hence, I recommend revisiting the transition implementation to allow react-transition-group to handle the entering/exiting states efficiently. Here's the modified version with my setTimeout hack: https://codesandbox.io/s/mui-animation-issue-505rj.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Grayscale to color image slider using JQuery, HTML, and CSS

Seeking assistance from the talented individuals here. Currently in the process of constructing a website for a client, and one of the key components on the index page is an image slider/fader that showcases a series of images. The client has requested a u ...

The hydration process encountered an error due to a discrepancy between the initial UI and the server-rendered content, specifically within the Next.js Tanstack table

I'm currently diving into learning the latest version of the Tanstack Table v8 React table component. While I am able to successfully render the columns and data in the table, I encountered an error message that says: "Hydration failed because the i ...

Ways to ensure all tabs on the Nav bar are evenly distributed across the width of the screen

Hello! I am in the process of creating my first website, and it's going to be a D&D character randomizer. Currently, I'm working on the Nav-bar section and I have four bars that I want to fit across the screen equally, regardless of the scree ...

Adding a half circle connector in HTML can be accomplished by using SVG (Scal

My task is to replicate the construction shown in this image: I have written the entire code but I am unsure how to include a half circle next to the full circle and connect it with a line connector. Here is my code: .ps-timeline-sec { position: rela ...

Passing data from React Native to an Android activity: Tips and tricks

I am currently working on integrating React Native into an existing Android app, and I need to send a rowId from a ListView in React Native to a method in my Android activity. This is to facilitate further processing within the app. Any suggestions on ho ...

Cached images do not trigger the OnLoad event

Is there a way to monitor the load event of my images? Here's my current approach. export const Picture: FC<PictureProps> = ({ src, imgCls, picCls, lazy, alt: initialAlt, onLoad, onClick, style }) => { const alt = useMemo(() => initial ...

Refresh meta tags in a Next.js/React app

In the process of optimizing a website for SEO, I am facing the challenge of updating the meta description's content dynamically without using any third-party libraries. While I have successfully implemented the functionality, it currently requires a ...

Issue with implementing LocomotiveScroll on a sticky element

Working on a personal project, I have been experimenting with Nextjs and locomotive-scroll. I am attempting to use position:sticky; along with data-scroll-sticky, but unfortunately the desired functionality is not working as expected. Here is the code snip ...

Tips on sending various properties to makeStyles() using TypeScript

After learning how to pass 1 prop to makeStyle() from a resource, I decided to try passing in 2 props for my project. However, I encountered an error stating cannot find name 'props'. Any assistance on this issue would be greatly appreciated! con ...

Having trouble figuring out what is causing non-serializable error in Redux (Redux error)

Initially, my react-native application was functioning smoothly and this particular page (screen) had been developed and working perfectly. However, out of the blue, it has started to encounter an error related to non-serializable data in the 'save_re ...

npm encountered an error: EPERM - Unable to perform the operation 'RENAME' due to insufficient permissions

Every time I run npm install, I encounter the following error: npm ERR! code EPERM npm ERR! syscall rename npm ERR! path C:\Users\Vaneeza10698\Downloads\ServiceQualityFinal\react-reduction\node_modules\npm\node_modul ...

The number I clicked is not displaying on the screen in my React project

When attempting to display the clicked number on the screen, it is not appearing. However, it does show in the console. I am unsure of how to fix this issue; the number should be displayed on the screen next to "Number:" import { render } from "@testing-li ...

Importing vs Referencing Videos in Next.js - What is the Best Practice for Video Integration in Next.js?

I'm looking to use a video as a background in my banner design. Typically, with images in Next.js, I would import them and then pass the import as src (for example: import img from '@images/about-us-page/img.svg'). However, when attempting ...

Issues arising from the arrangement of the menu bar

I created a navigation bar using an unordered list (ul) with list items (li). However, I encountered an issue where the items were displaying in reverse order until I applied the following CSS: .nav-bar .btn{ float: left; } .nav-bar u ...

What is preventing useEffect from accessing my state variable within a return statement?

I'm struggling to figure out why my useEffect() React function is unable to access my Component's state variable. My goal is to create a log when a user abandons creating a listing in our application and navigates to another page. I've imple ...

To replace basic SVG icons upon clicking with the use of HTML, CSS, and jQuery - here's how!

I have a simple question about inline SVG icons and how to handle them in HTML, CSS, and jQuery. Despite reading numerous resources, I still struggle with implementing the functionality where clicking on one icon changes it to another. My objective: To to ...

When attempting to launch the development server using 'npm start', an error is encountered preventing the server from starting

Recently delving into node and react, I encountered an issue while trying to launch a local development server for my react application. Upon initiating the process with npm start, an error was thrown. The console output is as follows: 0 info it worked if ...

What is preventing the input box from shrinking further?

I created a search box using CSS grid that is supposed to shrink when the page is resized. However, it doesn't seem to shrink as much as I expected it to. Here is how it appears when fully extended: https://i.stack.imgur.com/tPuCg.png And here is how ...

My code gets disrupted when I switch between ids and classes

I'm currently learning about javascript and jquery, attempting to implement various scripts. I successfully executed the following script: jQuery(document).ready(function($) { var scroll_start = 0; var startchange = $('#homepage-header' ...

InitAuth0 Auth0 encountering deepPartial error in Next.js with TypeScript setup

Having some trouble setting up auth0 with nextjs using typescript. When I try to initialize Auth0, I encounter an error regarding deep partials, Argument of type '{ clientId: string; clientSecret: string; scope: string; domain: string; redirectUri: st ...