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:
@@ -5,7 +5,7 @@
|
||||
// sayfalanır ("daha fazla göster" /api/programlar'dan akıtır). Her satırın "+"
|
||||
// butonu programı manuel 24'lük listeye ekler (bkz. manuel-liste/store).
|
||||
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
ChevronDown,
|
||||
@@ -119,6 +119,13 @@ export function ProgramTablosu({
|
||||
}
|
||||
}
|
||||
|
||||
// Görünmeyen sekmeler dahil 3 dilimin spread'i her render'da tekrarlanmasın
|
||||
const tumSatirlar = useMemo(() => {
|
||||
const r = {} as Record<DilimKey, ProgramListeSatiri[]>;
|
||||
for (const d of DILIMLER) r[d.key] = [...sonuclar[d.key], ...ekstra[d.key]];
|
||||
return r;
|
||||
}, [sonuclar, ekstra]);
|
||||
|
||||
async function dahaFazlaYukle(dilim: DilimKey, offset: number) {
|
||||
setYuklenen(dilim);
|
||||
try {
|
||||
@@ -174,7 +181,7 @@ export function ProgramTablosu({
|
||||
</TabsList>
|
||||
|
||||
{DILIMLER.map((d) => {
|
||||
const satirlar = [...sonuclar[d.key], ...ekstra[d.key]];
|
||||
const satirlar = tumSatirlar[d.key];
|
||||
const toplam = sonuclar.toplam[d.key];
|
||||
return (
|
||||
<TabsContent key={d.key} value={d.key}>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { UniLogo } from "@/components/uni-logo";
|
||||
@@ -29,6 +30,7 @@ import {
|
||||
sonucaGitIste,
|
||||
useTercihProfili,
|
||||
} from "../hooks/use-tercih-profili";
|
||||
import type { TercihProfili } from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||
|
||||
const RISK_STIL: Record<RiskSeviyesi, { nokta: string; kenar: string }> = {
|
||||
guvenli: { nokta: "bg-emerald-500", kenar: "border-l-emerald-500" },
|
||||
@@ -176,7 +178,7 @@ export function SecimlerimPaneli() {
|
||||
|
||||
<ol className="flex flex-col gap-3">
|
||||
{liste.map((t, i) => (
|
||||
<SecimSatiri key={t.id} tercih={t} sira={i + 1} />
|
||||
<SecimSatiri key={t.id} tercih={t} sira={i + 1} profil={profil} />
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
@@ -196,14 +198,17 @@ function Istatistik({ etiket, deger }: { etiket: string; deger: number }) {
|
||||
);
|
||||
}
|
||||
|
||||
function SecimSatiri({
|
||||
// Profil parent'tan prop olarak gelir (parent zaten abone) + memo: 24 satırın
|
||||
// her biri ayrı store aboneliği kurup her profil olayında uyanıyordu.
|
||||
const SecimSatiri = memo(function SecimSatiri({
|
||||
tercih,
|
||||
sira,
|
||||
profil,
|
||||
}: {
|
||||
tercih: ManuelTercih;
|
||||
sira: number;
|
||||
profil: TercihProfili | null;
|
||||
}) {
|
||||
const profil = useTercihProfili();
|
||||
const degerlendirme = profil
|
||||
? tercihDegerlendir(
|
||||
{
|
||||
@@ -302,4 +307,4 @@ function SecimSatiri({
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// açık; Yapay Zeka katmanı kilitliBaslangic sonrasında ücretlidir (metin
|
||||
// sunucuda zaten maskelidir).
|
||||
|
||||
import { Fragment, useEffect, useRef, useState } from "react";
|
||||
import { Fragment, useEffect, useMemo, useRef, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { ChevronDown, Lock, MapPin, Rocket, Scale, ShieldCheck } from "lucide-react";
|
||||
import { UniLogo } from "@/components/uni-logo";
|
||||
@@ -121,14 +121,20 @@ export function RaporListesi({
|
||||
/** Detaydaki "Haritada gör" — il DB biçiminde */
|
||||
onHaritadaGor?: (il: string | null, sira: number) => void;
|
||||
}) {
|
||||
const [acik, setAcik] = useState<Set<number>>(new Set());
|
||||
// Lazy init: boş Set her render'da boşuna allocate edilmesin
|
||||
const [acik, setAcik] = useState<Set<number>>(() => new Set());
|
||||
const satirRefs = useRef(new Map<number, HTMLTableRowElement>());
|
||||
|
||||
// Haritadan gelen seçim: satırı aç ve görünür alana kaydır
|
||||
// Haritadan gelen seçim: satırın açılması render sırasındaki önceki-prop
|
||||
// deseniyle (effect'te setState çift render yaratıyordu); yalnızca DOM işi
|
||||
// olan scrollIntoView effect'te kalır.
|
||||
const [oncekiAcikSira, setOncekiAcikSira] = useState(acikSira);
|
||||
if (acikSira !== oncekiAcikSira) {
|
||||
setOncekiAcikSira(acikSira);
|
||||
if (acikSira != null) setAcik((mevcut) => new Set(mevcut).add(acikSira));
|
||||
}
|
||||
useEffect(() => {
|
||||
if (acikSira == null) return;
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setAcik((mevcut) => new Set(mevcut).add(acikSira));
|
||||
satirRefs.current
|
||||
.get(acikSira)
|
||||
?.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
@@ -145,12 +151,14 @@ export function RaporListesi({
|
||||
|
||||
const kilitIdx = kilitliBaslangic ?? rapor.tercihler.length;
|
||||
|
||||
const iller = new Set(
|
||||
rapor.tercihler
|
||||
.map((t) => rapor.programlar[t.programId]?.il)
|
||||
.filter((il): il is string => Boolean(il)),
|
||||
);
|
||||
const tekIl = iller.size === 1 ? [...iller][0] : null;
|
||||
const tekIl = useMemo(() => {
|
||||
const iller = new Set<string>();
|
||||
for (const t of rapor.tercihler) {
|
||||
const il = rapor.programlar[t.programId]?.il;
|
||||
if (il) iller.add(il);
|
||||
}
|
||||
return iller.size === 1 ? [...iller][0] : null;
|
||||
}, [rapor]);
|
||||
|
||||
const satir = (t: RaporSonuc["tercihler"][number], index: number) => {
|
||||
const p = rapor.programlar[t.programId];
|
||||
|
||||
Reference in New Issue
Block a user