The React component experiences flickering when the state is changed at the end of an animation

One of my latest projects involves creating a dynamic react slide show component using the following approach:

  • The component accepts an array of slides (react components) as a prop and displays each slide one at a time, starting from the first slide with index 0.
  • The current slide being displayed is visible while the other slides are hidden.
  • During navigation (triggering the slide animation), the destination slide is added to the wrapper. If the destination slide has a smaller index than the current slide (when clicking on next), it will animate in from the left with a position change from -100% to 0 (slide-in effect from left). On the other hand, if the destination slide comes after the current slide (a larger index in the slides array, when clicking on previous), it will have an animation from 100% to 0 (slide-in effect from right); meanwhile, the current slide will slide out either to the right or left based on its position.
  • Once the animation completes, the current slide will be swapped with the slided-in slide (the destination slide), and the previous current slide will be removed from view.

While working on the Slides component, I encountered a glitch where placing the destination slide before the current slide in the wrapper component caused flickering issues. However, positioning it after the current slide did not result in any problems.

I am curious to understand why this flickering behavior occurs. Can you help shed some light on this issue?

In the scenario where there is no flickering (animation slide placed after the current slide), here is a demo: https://stackblitz.com/edit/vitejs-vite-wnedwd?file=src%2FSlides.jsx

flickering issue observed when the animation slide precedes the current slide): demo: https://stackblitz.com/edit/vitejs-vite-hy6rsp?file=src%2FSlides.jsx

Answer №1

Difference in Display

During the running animation (phase A), both elements are visible in the DOM, but in its inactive state (phase N), only one is displayed.
When we consider "phase A", particularly the second element returned in the JSX.
In the first scenario, this element is the sole display during "phase N". However, in the second scenario, it differs and another element is shown instead.

Impact of Timing

If we enclose the code within a callback function using setTimeout, we can observe the screen display after the animation completes and both elements are present in the DOM during "phase A".

const animationEndCallback = useCallback(() => {
 setTimeout(()=> {
   setCurrentSlide(animSlide);
   setAnimSlide(null);
 },3000)
}, [animSlide]);

We notice that after the animation concludes, the second returned element always appears on the screen.

Explanation

This elucidates the situation.
There is a moment post-animation where the component hasn't yet re-rendered, and both elements exist in the DOM. This particular instant is when the code inside the callback function executes, hence, displaying the second returned element on the screen before the component's rerendering, leaving only the cloned element with currentSlide-1. This likely contributes to the flickering issue and explains why it doesn't occur in the initial example.

Reason Analysis

As someone relatively new to CSS, I simplified the code by removing the animation and incorporating both elements solely to comprehend why only the second one displays when both are available.

const [currentSlide, setCurrentSlide] = useState(1); // red one
const [animSlide, setAnimSlide] = useState(0); // pink one

return (
  //...
    <div className={styles.slides}>
      {React.cloneElement(slides[animSlide], { 
        className: `
        ${slides[animSlide]?.props?.className}
        ${styles.absolute}
       `,
      })}
      {React.cloneElement(slides[currentSlide], { 
        className: `
        ${slides[currentSlide]?.props?.className}
        ${styles.absolute}
       `,
      })}
    </div>
  //...
)

Upon trying out the above code, it becomes apparent that only the second element is visibly shown thus far. This occurrence transcends the animation impact. Nevertheless, upon toggling ${styles.absolute} for both elements, we discern that the last one receiving className={styles.absolute} or position: absolute gets priority in terms of visibility at runtime. This also clarifies why adjusting z-index following Hadi KAR's answer rectifies this issue.

absolute

The element is detached from the regular document flow, creating no space allocation in the page layout. Its positioning relates to the nearest positioned ancestor (if present) or the default containing block. Final placement depends on top, right, bottom, and left values.
This feature generates a unique stacking context provided z-index isn't auto. Absolute-positioned boxes don't merge margins with other margins.


Upon stripping off ${styles.absolute} from both elements resulting in neither having a position: absolute, one would anticipate both to appear. However, unexpected findings show only the first element being visible. Upon inspecting the parent div wrapper's CSS class styles.slides, it delineates flex: 1 alongside overflow: hidden.
If we simplify to:

.slides {
  flex: 1;
}

This setting organizes children vertically, stacked based on input order, each occupying the screen accordingly. Contrastingly, adding overflow: hidden causes concealment of the second element due to overflow.


In closure, heartfelt gratitude for raising this question as it fosters my learning journey in CSS 🙂

Answer №2

I tested out the code snippet and found a solution to eliminate the flickering effect. By setting the leaving slide's z-index to -1, you can ensure that regardless of the position of animSlide or currentSlide in your tree, the currentSlide will always remain at the back.

Slides.module.css

.back {
  z-index: -1;
}

Slides.js

//...
{React.cloneElement(slides[currentSlide - 1], {
          className: `
            ${styles.absolute} 
            ${styles.slide} 
            ${slides[currentSlide - 1]?.props?.className} 
            ${
              animSlide &&
              (animSlide < currentSlide
                ? styles.slide_right
                : styles.slide_left)
            }
            ${
              styles.back // add this line
            }
          `,
          onAnimationEnd: animationEndCallback,
        })}
//...

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

What is the best way to prevent certain bootstrap classes from being applied across all elements?

After testing my HTML and CSS code on my local computer, it looks perfect. However, when I upload it to a server, the appearance changes. You can see it in action here: . The issue seems to be related to some bootstrap classes being activated differently ...

Is it a problem to use class names in conjunction with styled components?

What are the implications of incorporating classnames within styled components? For example, in the snippet below: export const StyledNav = styled.nav` background-color: ${({ theme }) => theme.backgroundSecondary}; .logo-container { width: 201p ...

What is the best way to vertically align my image for perfect centering?

On one of my pages at , there is a section titled "FREE PICK UP AND RECYCLING OF LARGE QUANTITIES OF ELECTRONICS" with a picture of a truck. The image is perfectly centered vertically between the pictures on its left and right. Another page I have at fea ...

Having trouble with the unresponsive sticky top-bar feature in Zurb Foundation 5?

According to the information provided on this website, it is recommended to enclose the top-bar navigation within a div element that has both the class "contain-to-grid" and "sticky". However, I have noticed that while the "contain-to-grid" class exists in ...

When hovering over an image, CSS text will be displayed

Creating a CSS hover effect where text appears when the user hovers over a small thumbnail and a large image is displayed upon clicking. I have set up a modal for the image, but having difficulty with positioning the text. Currently, the text is located un ...

Unpacking nested objects using dynamically generated property names in a React state - a guide

Having trouble with using setState and figuring out how to destructure the object with a dynamic property name, denoted by id. The state looks like this after computation: { "inputConfig": { "5d4d684cadf8750f7077c739": { "0": "5d4d ...

Using Node.js and Express, the CSS does not load due to route parameters

I'm currently working on a basic website project using Node.js, Express, and the EJS template engine. I've run into an issue where my external CSS files are not loading properly on routes that contain route parameters. Here's a snippet of my ...

Instructions for repositioning a popup to a specific area on the screen: How can a popup be shifted to anchor on

Hey there, I wanted to share my website www.livehazards.com with you. Currently, I am working on relocating the popup box linked to each earthquake to the bottom right corner of the screen. Below is the code related to the popup box: .mapboxgl-popup-co ...

What is the most effective method for combining data from two APIs into a single React table?

Looking to combine data from 2 separate APIs that both have pagination capabilities. What is the most efficient method to present the merged data in a table? The data needs to be joined based on their unique id, however one API provides fewer records tha ...

Issues with Monospace Google Fonts on JSFiddle are preventing them from displaying correctly

It's strange how monospace fonts from Google Fonts don't display properly on JSFiddle. Only sans-serif and serif fonts seem to be functioning correctly. ...

Is it possible to animate share buttons using Framer Motion but staggering siblings?

I have a Share Icon that looks like: I'm looking to display the first 5 icons, with the 5th icon being the share icon. The remaining icons should appear below the share icon and expand to the right when someone taps or hovers over it (either tap or h ...

A step-by-step guide on disabling Google Analytics in a Next.js document.js file

Upon a user's initial visit to the website, they are presented with the option to either accept or decline tracking cookies. If the user declines, a cookie is set with the value of false. I am in need of a way to conditionally run the gtag script base ...

How can you programmatically deselect all checkboxes in a list using React hooks?

I am facing a challenge with my list of 6 items that have checkboxes associated with them. Let's say I have chosen 4 checkboxes out of the 6 available. Now, I need assistance with creating a button click functionality that will uncheck all those 4 sel ...

What is the best way to adjust the padding within a mat-expansion-panel-body?

I recently created an expansion panel that is working well, but I am having trouble removing a padding from the subpanel section. Despite my efforts, I haven't been able to find a suitable solution. Here's a link to the panel image: https://i.ss ...

Troubleshooting Bootstrap: Identifying errors in my bootstrap code

Could you help me diagnose the issue with this code? I have tailored it to be responsive on my tablet by using 12 columns for two divs, expecting the third div to be in a new row. However, upon checking my index.html on the tablet, all the divs are display ...

The transparency of an image is being lost when it is used in a modal

I am facing an issue when trying to append a modal that contains only a transparent gif. The problem arises when the transparent background of the gif is not displayed properly. There seems to be no issue with the transparency settings of the gifs themselv ...

I am having trouble getting my divs to show up side by side in one line

I am struggling to get the second div (right-column) to move next to the first div (left-column) in order to create 2 columns of links. I would prefer not to create a separate stylesheet for this small page. <div class="container" style="height: 700px; ...

Achieving uniform space between divs using flex

This is the layout setup I am aiming for using display:flex. My goal is to have text1 displayed at the top, text3 at the bottom, and text2 in the middle. https://i.sstatic.net/xaeaB.png However, the actual outcome looks like this: https://i.sstatic.net/ ...

I'm having trouble displaying the image in its full size using thickbox with jquery

Having trouble displaying images in their actual size with thickbox. Any solutions? <html> <head> <link rel="stylesheet" type="text/css" href="style.css"> <title>WildFire</title> <script type="text/javascr ...

Having issues with jQuery not functioning on this specific web page across all browsers. The reason behind this puzzling dilemma remains elusive to me

I am experiencing issues with the functionality of my website. The buttons with + signs are meant to be drop-down toggles, and the mini-tabs below them should work as tabs. Additionally, the sample images at the bottom are not loading properly when clicked ...