feat: optimize Hero component by isolating typing effect state
Extracted the high-frequency typing animation logic into a new, memoized `TypedText` component. This prevents the entire `Hero` component (including the heavy `GradientBlinds`) from re-rendering on every character update. - Created `TypedText` component in `Hero.tsx` - Wrapped `TypedText` in `React.memo` - Moved `useTypingEffect` call into `TypedText` - Updated `Hero` to use `TypedText` Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
## 2024-05-22 - Missing Scripts and Environment
|
||||
**Learning:** The project lacks `lint` script in `package.json`. Running `pnpm lint` might invoke system tools (like Android Lint?) instead of failing or doing nothing useful. Always check `package.json` scripts first.
|
||||
**Action:** Use specific commands like `pnpm exec tsc --noEmit` or `npx vitest` as discovered/documented, rather than assuming standard scripts exist.
|
||||
|
||||
## 2024-05-22 - High-Frequency State Isolation
|
||||
**Learning:** High-frequency state updates (like typing effects) in large parent components (`Hero`) trigger massive unnecessary re-renders of expensive sub-trees (`GradientBlinds`, `Button`).
|
||||
**Action:** Isolate high-frequency state into small, leaf-node components (e.g., `TypedText`) and wrap them in `React.memo` if necessary, keeping the heavy parent static.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useState, useEffect, useRef, memo } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { motion } from "motion/react";
|
||||
import { useTranslation } from "../../i18n";
|
||||
@@ -9,6 +9,21 @@ import styles from "./Hero.module.css";
|
||||
|
||||
const GRADIENT_COLORS = ["#26a269", "#8ff0a4"];
|
||||
|
||||
interface TypedTextProps {
|
||||
words: string[];
|
||||
}
|
||||
|
||||
const TypedText = memo(({ words }: TypedTextProps) => {
|
||||
const { text } = useTypingEffect({
|
||||
words,
|
||||
typingSpeed: 80,
|
||||
deletingSpeed: 40,
|
||||
pauseDuration: 2500,
|
||||
});
|
||||
|
||||
return <>{text}</>;
|
||||
});
|
||||
|
||||
export function Hero() {
|
||||
const { t } = useTranslation();
|
||||
const [showScrollIndicator, setShowScrollIndicator] = useState(true);
|
||||
@@ -29,13 +44,6 @@ export function Hero() {
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
const { text } = useTypingEffect({
|
||||
words: t.hero.rotatingWords,
|
||||
typingSpeed: 80,
|
||||
deletingSpeed: 40,
|
||||
pauseDuration: 2500,
|
||||
});
|
||||
|
||||
return (
|
||||
<section className={styles.hero}>
|
||||
<div
|
||||
@@ -100,7 +108,7 @@ export function Hero() {
|
||||
>
|
||||
<span>{t.hero.tagline}</span>
|
||||
<span className={styles.typed}>
|
||||
{text}
|
||||
<TypedText words={t.hero.rotatingWords} />
|
||||
<span className={styles.cursor}>|</span>
|
||||
</span>
|
||||
</motion.div>
|
||||
|
||||
Reference in New Issue
Block a user