When the React component's state is updated, an animation is triggered in a subcomponent connected to the parent component

The ProjectsSection component is responsible for rendering a document section with a header and cards. The goal is to trigger a header animation only when the header becomes visible on the screen for the first time and have it run once. However, adding a state to the ProjectsSection component resulted in the header animation running on every state change. Surprisingly, only a part of the animation (the letters/spans that are children of h2) actually moves, while brackets/pseudo elements on h2 do not. CSS modules are used for styling in this project.

Attempts were made to wrap the SectionHeader component in React.memo, but it did not solve the issue.

Main component:

const ProjectsSection = () => {
  const [openProjectCardId, setOpenProjectCardId] = useState('');
  
  return (
    <section id="projects" className="section">
      <div className="container">
        <SectionHeader>Projects</SectionHeader>

        <div className={styles.content_wrapper}>
          {projects.map((project, ind) => (
            <Project
              key={project.id}
              project={project}
              ind={ind}
              isOpen={openProjectCardId === project.id}
              setOpenProjectCardId={setOpenProjectCardId}
            />
          ))}
        </div>
      </div>
    </section>
  );
};

SectionHeader component:

const SectionHeader = ({ children }) => {
  const headerRef = useIntersection(
    styles.sectionHeader__isVisible,
    { rootMargin: '0px 0px -100px 0px', threshold: 1 },
  );

  const textToLetters = children.split('').map((letter) => {
    const style = !letter ? styles.space : styles.letter;
    return (
      <span className={style} key={nanoid()}>
        {letter}
      </span>
    );
  });

  return (
    <div className={styles.sectionHeader_wrapper} ref={headerRef}>
      <h2 className={styles.sectionHeader}>{textToLetters}</h2>
    </div>
  );
};

Css

.sectionHeader_wrapper {
  position: relative;
  // other properties

  &:before {
    display: block;
    content: ' ';
    position: absolute;
    opacity: 0;
    // other properties
  }

  &:after {
    display: block;
    content: ' ';
    position: absolute;
    opacity: 0;
    // other properties
  }
}

.sectionHeader {
  position: relative;
  display: inline-block;
  overflow: hidden;
  // other properties
}

.letter {
  position: relative;
  display: inline-block;
  transform: translateY(100%) skew(0deg, 20deg);
}

.sectionHeader__isVisible .letter {
  animation: typeletter .25s ease-out forwards;
}

useIntersection hook

const useIntersection = (
  activeClass,
  { root = null, rootMargin = '0px', threshold = 1 },
  dependency = [],
  unobserveAfterFirstIntersection = true
) => {
  const elementRef = useRef(null);

  useEffect(() => {
    const options: IntersectionObserverInit = {
      root,
      rootMargin,
      threshold,
    };

    const observer = new IntersectionObserver((entries, observerObj) => {
      entries.forEach((entry) => {
        if (unobserveAfterFirstIntersection) {
          if (entry.isIntersecting) {
            entry.target.classList.add(activeClass);
            observerObj.unobserve(entry.target);
          }
        } else if (entry.isIntersecting) {
          entry.target.classList.add(activeClass);
        } else {
          entry.target.classList.remove(activeClass);
        }
      });
    }, options);

    // if (!elementRef.current) return;
    if (elementRef.current) {
      observer.observe(elementRef.current);
    }
  }, [...dependency]);

  return elementRef;
};

Answer №1

After much investigation, I have successfully resolved the issue at hand. The root cause appeared to be the textToLetters variable being recreated whenever the parent component's state changed. By implementing useMemo to maintain this value, the animation no longer gets triggered.

const convertTextToLetters = (text) =>
  text.split('').map((letter) => {
    const style = !letter ? styles.space : styles.letter;
    return (
      <span className={style} key={nanoid()}>
        {letter}
      </span>
    );
  });

const SectionHeading= ({ children }) => {
  const headerRef = useIntersection(
    styles.heading__isVisible,
    { rootMargin: '0px 0px -100px 0px', threshold: 1 },
    [children]
  );
  const letters = React.useMemo(() => convertTextToLetters(children), [children]);

  return (
    <div className={styles.heading_wrapper} ref={headerRef}>
      <h2 className={styles.heading}>{letters}</h2>
    </div>
  );
};

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

The color scheme of the header in Material UI Tables

I am facing a challenge in customizing the mui TableHeader with a dark background. Despite trying to use @mui/styles, I have been unsuccessful due to React version 18 restrictions. Can anyone offer suggestions on how to achieve this? https://i.stack.imgu ...

Is it feasible to generate emojis using a gulp Icon-font task?

I'm in the process of creating a script that can generate fonts from SVG images, and so far it's been successful (using similar code to what's provided on https://www.npmjs.com/package/gulp-iconfont) Now I have a more specific question - is ...

Compiling React Native in Node.JS encountered a critical error

I'm in the process of putting together a react native guide for creating forms, which can be accessed here: - https://github.com/smhatre59/cloudstorage However, when I try to compile using "npm run-script build" or even for development using "npm sta ...

Bottom section of a multi-level website with horizontal dividers

I was struggling with aligning divs next to each other, but I managed to find a solution. The only issue is that the text now aligns to the right instead of going below as it should. Typically this wouldn't be an issue since all content is within the ...

Steps for properly inserting a hyperlink:

I'm in need of assistance. I have a grid containing several images that I want to make clickable by adding anchor tags, but when I do this, the images seem to disappear completely. I suspect there's an issue with them being inside the container d ...

Tips on modifying responsive design with jQuery

I have implemented a responsive design approach with the following CSS code: @media all and (max-width:799px){ .bigFont{ font-size: 28px; } } @media all and (min-width:800px){ .bigFont{ font-size: 48px; } } If I want to modify the font size using ...

Error encountered during the construction of the dojo due to a CSS syntax

I am currently using version 1.9.3 of dojo, and I have encountered an issue with the css file when trying to build dojo using build.bat. The code in my css file is as follows: @import url("TimeDriverCommon.css"); @import url("DialogCommon.css"); @import ...

The absence of the 'Access-Control-Allow-Origin' CORS header was noticed in the response from the Wikipedia API

Attempting to retrieve data from the Wikipedia API using axios in reactJS. Here is my request: axios.get('https://en.wikipedia.org/w/api.php?action=opensearch&search=lol&format=json&callback=?') .then((response) => { c ...

adjust the size of a form field to match the background image dimensions

I'm encountering an issue while trying to solve this particular challenge: My goal is to integrate a login box onto a full-screen image. Although I've come across numerous methods for incorporating an image into a form field, my task requires me ...

Starting the Android System Magnifier with a Click: A Step-by-Step Guide

Is there a way to incorporate a magnifying glass into an HTML page using jQuery or within an Android app? Ideally, it would be for a single picture. In my application, I am displaying an HTML file from my assets in a webview. The HTML page utilizes jQuery ...

Could we customize the appearance of the saved input field data dropdown?

I'm currently working on styling a form that includes an input field. The challenge I'm facing is that the input field needs to be completely transparent. Everything works fine with the input field until I start typing, which triggers the browser ...

Is there a way to adjust the layout of the dynamic card's columns?

Is there a way to display the card with 2 columns for the first card and then another 2 columns for the second card? Right now, it looks like this: https://i.stack.imgur.com/zu5PI.png However, I would like it to look more like this: https://i.stack.imgur ...

loop not functioning properly with file type input

Having trouble uploading an image and copying it into a folder named images within a loop. Can you assist with solving this issue? Here's my code: $sql="SELECT * FROM product"; $q=$conn->query($sql); while($r=$q->fetch(PDO::FETCH_ASSOC)) { $cod ...

Determine the selected value of a checkbox in a React JS component

Can anyone help me with getting the value of a checkbox in React.js? It always returns false (a boolean value). Here is a snippet of my code: export default class CreateMaterial extends Component { constructor(props) { super(props); thi ...

How to properly Open a "div" Element by its ID in ReactJS

Today, I want to share an issue that I've been facing. To simplify things, I have mapped a BDD, The result of this mapping is displayed in multiple cards, There's a button that when clicked opens up more details on the map, But here's wh ...

What is preventing me from creating accurate drawings on canvas?

I'm currently working on a paint application and facing an issue. When I place the painting board on the left side of the screen, everything works fine and I can draw without any problems. However, when I move it to the right side of the screen, the m ...

DOMPDF - Comparing background URL rendering between Linux and Windows

When converting HTML to PDF, my image doesn't show on a Linux server. On localhost, the image displays properly. Here is my CSS code: background: red url("<?php echo __DIR__ ?/my_img.png"); However, on the Linux server, the image does not appea ...

Difficulty positioning CSS, aligning flex-box grid with floating text

My attempt to include an aside section is causing alignment issues with the navigation menu and the aside itself. The problem seems to be related to using float:left for the 200x200 images. I want the layout to be well-positioned like the expected image. I ...

What could be causing the 404 error message in Backend deployment on Vercel?

Hello everyone, I'm currently in the process of deploying the backend for my MERN application on Vercel. Prior to this attempt, I successfully deployed a small todo project using MERN on Vercel. Everything worked fine then, and I followed the same st ...

Turn off hover opacity effect for a specific image

Currently, I've implemented a hover opacity effect on my images using the following CSS: img { opacity: 1.0; filter: alpha(opacity=1.0); } img:hover { opacity: 0.6; filter: alpha(opacity=0.6); } However, I now have a specific image for whi ...