perf: deterministik dekor memo'ları ve küçük render süpürmeleri (Faz 7)
Some checks failed
Deploy / deploy (push) Has been cancelled
Some checks failed
Deploy / deploy (push) Has been cancelled
- pixel-decor: PixelField/PixelDivider piksel üretimi useMemo([seed]) — 504 iterasyon + ~150 element çekmece reorder'ının her karesinde yeniden üretiliyordu - rapor-listesi: acikSira effect'i önceki-prop desenine döndü (çift render ve eslint-disable kalktı), Set lazy init, tekIl useMemo - program-tablosu: 3 dilimin spread'i useMemo - typing-animation: displayedText state yerine türetme; kaynak reset'i render sırasında — tick başına state yazımı ve effect bağımlılığı azaldı - site-top-banner: saniyelik geri sayım tiki startTransition'da - program-liste-verileri: sparkline 6 dizi geçişi tek döngüde - secimlerim-paneli: SecimSatiri profili prop'tan alır + memo (24 ayrı store aboneliği kalktı) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -81,7 +81,6 @@ export function TypingAnimation({
|
||||
Component
|
||||
] as TypingAnimationMotionComponent
|
||||
|
||||
const [displayedText, setDisplayedText] = useState<string>("")
|
||||
const [currentWordIndex, setCurrentWordIndex] = useState(0)
|
||||
const [currentCharIndex, setCurrentCharIndex] = useState(0)
|
||||
const [phase, setPhase] = useState<"typing" | "pause" | "deleting">("typing")
|
||||
@@ -109,21 +108,32 @@ export function TypingAnimation({
|
||||
[words, children]
|
||||
)
|
||||
|
||||
// Kaynak metin değişince animasyon baştan başlar — bilinçli reset
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setDisplayedText("")
|
||||
// Kaynak metin değişince animasyon baştan başlar — önceki-prop deseniyle
|
||||
// render sırasında (effect'te setState çift render yaratıyordu)
|
||||
const [oncekiKaynak, setOncekiKaynak] = useState(animationSourceKey)
|
||||
if (animationSourceKey !== oncekiKaynak) {
|
||||
setOncekiKaynak(animationSourceKey)
|
||||
setCurrentWordIndex(0)
|
||||
setCurrentCharIndex(0)
|
||||
setPhase("typing")
|
||||
}, [animationSourceKey])
|
||||
}
|
||||
|
||||
// Görünen metin state değil türetme: tick başına ekstra state yazımı ve
|
||||
// grapheme dizisi allocation'ı kalkar (rerender-derived-state-no-effect)
|
||||
const displayedText = useMemo(
|
||||
() =>
|
||||
Array.from(wordsToAnimate[currentWordIndex] || "")
|
||||
.slice(0, currentCharIndex)
|
||||
.join(""),
|
||||
[wordsToAnimate, currentWordIndex, currentCharIndex]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
let timeout: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
if (!azMotion && shouldStart && wordsToAnimate.length > 0) {
|
||||
const timeoutDelay =
|
||||
delay > 0 && displayedText === ""
|
||||
delay > 0 && currentCharIndex === 0
|
||||
? delay
|
||||
: phase === "typing"
|
||||
? typingSpeed
|
||||
@@ -138,9 +148,6 @@ export function TypingAnimation({
|
||||
switch (phase) {
|
||||
case "typing":
|
||||
if (currentCharIndex < graphemes.length) {
|
||||
setDisplayedText(
|
||||
graphemes.slice(0, currentCharIndex + 1).join("")
|
||||
)
|
||||
setCurrentCharIndex(currentCharIndex + 1)
|
||||
} else {
|
||||
if (hasMultipleWords || loop) {
|
||||
@@ -159,9 +166,6 @@ export function TypingAnimation({
|
||||
|
||||
case "deleting":
|
||||
if (currentCharIndex > 0) {
|
||||
setDisplayedText(
|
||||
graphemes.slice(0, currentCharIndex - 1).join("")
|
||||
)
|
||||
setCurrentCharIndex(currentCharIndex - 1)
|
||||
} else {
|
||||
const nextIndex = (currentWordIndex + 1) % wordsToAnimate.length
|
||||
@@ -184,7 +188,6 @@ export function TypingAnimation({
|
||||
phase,
|
||||
currentCharIndex,
|
||||
currentWordIndex,
|
||||
displayedText,
|
||||
wordsToAnimate,
|
||||
hasMultipleWords,
|
||||
loop,
|
||||
|
||||
Reference in New Issue
Block a user