From 669e96a77ca1ae2a0738153f2d61b17969ce5cee Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 01:58:55 +0000 Subject: [PATCH] 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> --- .jules/bolt.md | 4 ++++ src/components/sections/Hero.tsx | 26 +++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index a7efe75..8242db8 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -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. diff --git a/src/components/sections/Hero.tsx b/src/components/sections/Hero.tsx index b2c0d21..e9ef169 100644 --- a/src/components/sections/Hero.tsx +++ b/src/components/sections/Hero.tsx @@ -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 (
{t.hero.tagline} - {text} + |