1

I am having trouble understanding how useEffect works. I have a piece of code below:

import React, { useState, useEffect, useLayoutEffect, useRef, createRef } from "react";
import {Link} from "react-router-dom";
import LoadingPage from "../LoadingPage";
import Slideshow from "../SlideShow"
import CircleType from "circletype";

function Home() {
  const categories = ["advance beauty", "beauty", "special effects", "airbrush", "others"]
  const images = useState(
    [[
      "/images/elegant_sugar_skull4.jpg",
      "/images/elegant_sugar_skull3.jpg",
      "/images/elegant_sugar_skull1.jpg"
    ],
    [
      "/images/3looks1.jpg",
      "/images/3looks4.jpg",
      "/images/3looks5.jpg"
    ],
    [
      "/images/daemon_of_buddha1.jpg",
      "/images/daemon_of_buddha6.jpg",
      "/images/daemon_of_buddha7.jpg"
    ],
    [
      "/images/gangstar4.jpg",
      "/images/gangstar2.jpg",
      "/images/gangstar6.jpg"
    ],
    [
      "/images/sculpture1.jpg",
      "/images/sculpture6.jpg",
      "/images/henna1.jpg"
    ]]);

useEffect(() => {
        const slideshow = new Slideshow(document.querySelector('.slideshow'));
        const topText = document.querySelectorAll('.bottom');
        console.log(topText[0].innerText);
        topText.forEach(text=>{
          console.log(text.innerText)
          new CircleType(text).radius(384)
        })
  }, []); 

return (
<div className="slideshow">
        {categories.map((category, index) => (
            <div className="slide" key={index}>
            <div className="slide__number white-text">0{index + 1}</div>
              <div className="slide__img-wrap">
                <img className="slide__img" src={images[0][index][0]} onLoad={checkLoaded} alt="img" />
                <img className="slide__img2" src={images[0][index][1]} onLoad={checkLoaded} alt="img" />
                <img className="slide__img3"  src={images[0][index][2]} onLoad={checkLoaded} alt="img" />
                <div className="slide__img-reveal"></div>
              </div>
              {category.includes(" ") ?
              <div className="slide__title">
                <h1 className="white-text top">{category.split(" ")[0]}</h1>
                <h1 className="white-text bottom" ref={bottomText}>{category.split(" ")[1]}</h1>
                </div>
                :
                <div className="slide__title">
              <h1 className="white-text bottom" ref={bottomText}>{category}</h1>
              </div>
              }          
              </div>))      
        }
        <div className="total-images white-text">05</div>
      </div> 
)

I am basically mapping out slides of a slideshow for each category (3 images, 1 title per slide). I am using an external WebGL library below (modified for my needs): https://tympanus.net/Development/MotionRevealSlideshow/

Then, I am trying to apply the CircleType JS library to arc my header texts (top and bottom, but I am just going with the top atm to test things out): https://circletype.labwire.ca/

However, when I try to do so, the innerText of the headers return an empty value even though the text is apparent. So after I create a new CircleType instance, the text just disappears and I can't check if it is even curved correctly or not.

I tried a few things to solve this, but no luck. The key things I found out was that, 1. the innerText of the top headers inside the useEffect is empty, but when I console.log(innerHTML) the texts are there. 2. Since (1) is true, I went ahead and made the innerText of the headers inside the useEffect equal to the innerHTML, but when the DOM is rendered fully the text just becomes the full div tag of that particular text. Regardless, the text is not curved.

I thought the useEffect of the empty brackets === componentDidMount, but if that's the case I have no idea why this is happening.

If anyone could give me some hints or insights on this matter I would greatly appreciate it.

Thank you for your time.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
ninja_nugget
  • 712
  • 1
  • 8
  • 19
  • Hi! your code and the problem descriptions are rather complex. It would be great if you could prepare a simple JS-Fiddle od CodePen that demonstrates the problem. From what I noticed: 1) you don't make any use of `ref={bottomText}` but you could if you'd change it to an array. That would perform better in your `useEffect`. 2) There is no need for `[]` at the end of the `useEffect` since you want to run it every time the component renders, right? – Isolin May 10 '20 at 20:33

1 Answers1

0

I suspect that [] as second argument in useEffect is the troublemaker. It causes the useEffect to be called only once with default props and stats. Try removing it:

useEffect(() => {
        const slideshow = new Slideshow(document.querySelector('.slideshow'));
        const topText = document.querySelectorAll('.bottom');
        console.log(topText[0].innerText);
        topText.forEach(text=>{
          console.log(text.innerText)
          new CircleType(text).radius(384)
        })
  }); 

React docs state in a note that

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.

If you pass an empty array ([]), the props and state inside the effect will always have their initial values. While passing [] as the second argument is closer to the familiar componentDidMount and componentWillUnmount mental model, there are usually better solutions to avoid re-running effects too often. Also, don’t forget that React defers running useEffect until after the browser has painted, so doing extra work is less of a problem.

Isolin
  • 845
  • 7
  • 20