我不知道该如何描述这个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>
</>
);
}
1条答案
按热度按时间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。