javascript React不会渲染由代码创建的UI的旧部分

wfveoks0  于 2023-03-21  发布在  Java
关注(0)|答案(1)|浏览(89)

我不知道该如何描述这个bug,所以我只想解释一下发生了什么。
这个版本过于简化了,没有引起bug,但它有助于理解我的代码:
我有一个包含三个字符串strArr的数组。我以这种方式呈现它们:

<p>{strArr[0] + strArr[1]} <span>{strArr[2]}</span></p>

现在,如果所有的值都是静态的,一切都可以呈现。但是,在我的例子中,我首先将文本赋予strArr[0],然后在一秒钟后,我将文本分配给strArr[1],然后,在另一秒钟后,当我将文本分配给strArr[2]时,来自{strArr[0] + strArr[1]}的文本停止呈现。
实际上,我正在尝试制作一个CEO友好的输入动画,下面是我的实际代码(如果有必要,我使用Next.js 13.2中的app目录)。
现在你可以在这里看到这个bug的样子,如果我还没有修复它,下面是我的代码。

"use client";
import Style from "./page.module.css";
import Link from "next/link";
import { useEffect, useState } from "react";

type Props = {};

export default function TypeIn({}: Props) {
  // Animation length in ms
  const aniLength = 2000;
  const text: [string, string, string] = [
    "Hello, my name is",
    " ",
    "Dennis Lonoshchuk",
  ];
  const fullText = text[0] + text[1] + text[2];
  const [displayedText, setDisplayedText] = useState<[string, string, string]>([
    "",
    "",
    "",
  ]);

  useEffect(() => {
    const iterAmount = fullText.length; // subtract 1 because we start at 0 in the array
    let nextIter = aniLength / fullText.length;
    let i = 1;
    const interval = setInterval(() => {
      if (i <= text[0].length) {
        setDisplayedText([fullText.slice(0, i), "", ""]);
      } else if (i <= text[0].length + text[1].length) {
        setDisplayedText([
          displayedText[0],
          fullText.slice(text[0].length, i),
          "",
        ]);
      } else if (i <= text[0].length + text[1].length + text[2].length) {
        setDisplayedText([
          displayedText[0],
          displayedText[1],
          fullText.slice(text[0].length + text[1].length, i),
        ]);
      }
      if (i >= iterAmount) {
        clearInterval(interval);
      } else {
        i++;
      }
    }, nextIter);
  }, []);

  return (
    <>
      <h1 className="hidden">{fullText}</h1>
      <p
        className={`font-semibold text-4xl sm:text-6xl md:text-7xl
text-stroke-2 pt-[max(76px,32vh)]`}
      >
        {displayedText[0] + displayedText[1]}
        <Link
          href="/contact"
          className="text-transparent dark:text-stroke-white text-stroke-black text-stroke-2
dark:hover:text-white hover:text-black
transition-all ease-in-out duration-300"
        >
          {displayedText[2]}
        </Link>
      </p>
    </>
  );
}
tjjdgumg

tjjdgumg1#

为了直接回答你问的问题,你的useEffect钩子缺少依赖项,它应该是[displayedText, fullText, text]
React组件多次“渲染”,这意味着在你的例子中TypeIn会运行多次。每次,函数和变量都会在组件/函数体中重新创建。displayedText会不断更新,但useEffect回调函数永远不会重新创建,因为你告诉React不要重新创建它,因为它没有依赖关系,通过将依赖关系数组标记为[]
您可能已经知道闭包,但如果您不知道,您应该研究它们。当您调用setState()时,组件会使用新的displayedText集重新呈现,但不会重新创建useEffect回调。另一种描述闭包的常用方法是函数(useEffect回调)保持外部作用域(组件函数)中变量的“快照”。
你遇到的核心问题是在useEffect()内部调用setInterval(),这在React中是一件很棘手的事情。你应该使用来自"Making setInterval Declarative with React Hooks"的代码(理想情况下读取所有代码),而不是像现在这样调用setInterval
如果你还没有,你也应该在你的项目中安装eslint,并确保它在你输入的时候在你的编辑器中内联显示错误和警告。你通常不应该忽略React依赖警告,除非你有一个非常具体的用例/控制流,你知道它不会导致bug。

相关问题