Refactoring-Skill
Verwenden Sie diese Skill beim Refactoring von Features, um sie an etablierte Muster anzupassen, Duplikate zu entfernen und die Code-Wiederverwendung zu verbessern.
Für die Frage, ob UI zur pattern-library oder zur lokalen Komposition gehört, verwenden Sie die design-system-Skill.
Analyse vor dem Refactoring
1. Ähnliche Features identifizieren
- Finden Sie vergleichbare Features in der Codebasis mit ähnlicher Funktionalität
- Vergleichen Sie Verzeichnisstrukturen, Komponentenmuster und die Nutzung von Hilfsprogrammen
- Suchen Sie nach Features mit ähnlicher Funktionalität, aber unterschiedlichen Implementierungen
- Verwenden Sie semantische Suche, um verwandte Features zu finden: "features with similar table/data grid patterns"
2. Gelegenheiten für Duplikate finden
Suchen Sie nach doppelten Code-Mustern:
- Komponenten: Suchen Sie nach ähnlichen Komponentendateien in verschiedenen Features (gleicher Name oder ähnliche Funktionalität)
- Hooks: Überprüfen Sie auf doppelte Hook-Logik (Tabellen-Hooks, Filter-Hooks, Aggregations-Hooks)
- Hilfsfunktionen: Finden Sie doppelte Hilfsfunktionen (Aggregationen, Formatierer, Validierer, Transformer)
- Styles: Identifizieren Sie doppelte
sx-Prop-Muster oder Inline-Styles - Konstanten: Überprüfen Sie auf doppelte Konstanten oder Konfigurationsobjekte
- Typen: Suchen Sie nach doppelten Typdefinitionen oder Interfaces
3. Gemeinsame Hilfsfunktionen prüfen
Bevor Sie neue Hilfsfunktionen erstellen, überprüfen Sie vorhandene:
- Suchen Sie in
@/util/-Verzeichnissen nach domänenspezifischen Hilfsfunktionen - Überprüfen Sie
@/util/datesauf Datumsformatierungsfunktionen - Überprüfen Sie
@/util/numbersauf Zahlenformatierungsfunktionen - Überprüfen Sie
@/components/auf wiederverwendbare Komponenten - Überprüfen Sie
pattern-libraryauf Design-System-Komponenten - Verwenden Sie grep/search, um vorhandene Implementierungen zu finden, bevor Sie neue erstellen
Refactoring-Prozess
Schritt 1: Gemeinsamen Code extrahieren
Wenn Duplikate gefunden werden:
-
Erstellen Sie gemeinsame Hilfsfunktionen in den entsprechenden
@/util/-Verzeichnissen:// Beispiel: util/[domain]/[category].ts export const sharedFunction = () => { ... }; export const sharedConstant = { ... }; -
Aktualisieren Sie alle Features, um aus dem gemeinsamen Speicherort zu importieren:
// Feature-spezifische Datei - re-exportieren aus gemeinsamem Speicherort export { sharedFunction, sharedConstant } from "@/util/[domain]/[category]"; -
Entfernen Sie doppelte Implementierungen aus den Feature-Verzeichnissen
-
Aktualisieren Sie alle Imports in der gesamten Codebasis
Schritt 2: Komponenten konsolidieren
Wenn doppelte Komponenten gefunden werden:
- Identifizieren Sie die vollständigste/feature-reichste Version (überprüfen Sie Props, Funktionalität, Tests)
- Extrahieren Sie sie an einen gemeinsamen Speicherort (
@/components/oder feature-agnostischer Speicherort) - Machen Sie Props flexibel, um alle Anwendungsfälle zu unterstützen (verwenden Sie Union-Typen, optionale Props oder Generics)
- Aktualisieren Sie Imports in allen Features, die die Komponente verwenden
- Löschen Sie doppelte Komponentendateien
- Überprüfen Sie, ob alle Verwendungen noch korrekt funktionieren
Schritt 3: Ladepatterns angleichen
Für Ladezustände:
- Überprüfen Sie
loading.tsx-Dateien - sollten einfache Skelette sein, keine vollständigen Seitenlayouts - Entfernen Sie komponenteninterne Skelett-Loader - verlassen Sie sich für das anfängliche Laden auf das seitenweite
loading.tsx - Während erneuter Abrufe: Zeigen Sie vorhandene Daten an, keine Skelette (es sei denn, UX erfordert explizit etwas anderes)
- Passen Sie sich Mustern aus ähnlichen Features in der Codebasis an
- Verwenden Sie Next.js App Router-Konventionen -
loading.tsxübernimmt das anfängliche Laden, Komponenten übernehmen erneute Abrufzustände
Schritt 4: Styles konsolidieren
Für Styling:
- Extrahieren Sie wiederverwendbare Stilmuster in gemeinsame Komponenten oder Stil-Hilfsfunktionen
- Entfernen Sie Inline-
sx-Props, wenn eine gemeinsame Komponente existiert - Verwenden Sie Design-System-Komponenten aus
pattern-libraryanstelle von benutzerdefinierten Styles - Überprüfen Sie auf doppelte Stilobjekte, die gemeinsame Hilfsfunktionen oder Theme-Erweiterungen sein könnten
- Folgen Sie Material-UI-Mustern - verwenden Sie Theme-Werte, vermeiden Sie magische Zahlen
Schritt 5: Musterangleichung überprüfen
Stellen Sie Konsistenz mit etablierten Features sicher:
- Vergleichen Sie Verzeichnisstrukturen - sollten ähnlichen Mustern folgen (
_components/,_hooks/,_utils/) - Überprüfen Sie Hook-Muster - einfachere Hooks sind in Ordnung, wenn sich die Feature-Anforderungen unterscheiden
- Überprüfen Sie den Datenfluss - sollte nach Möglichkeit ähnlichen Features entsprechen
- Überprüfen Sie die Komponentenzusammensetzung - sollte ähnliche Muster verwenden (Wrapper, Komposition usw.)
- Überprüfen Sie Namenskonventionen - folgen Sie etablierten Mustern (PascalCase für Komponenten, camelCase für Hooks)
Schritt 6: Konsolidierung von Hilfsfunktionen
Für Hilfsfunktionen:
- Überprüfen Sie, ob die Funktion existiert zuerst in gemeinsamen Hilfsfunktionen (Codebase durchsuchen)
- Vergleichen Sie Implementierungen - verwenden Sie die vollständigere/robustere Version
- Konsolidieren Sie Duplikate - verschieben Sie sie an einen gemeinsamen Speicherort, aktualisieren Sie alle Imports
- Dokumentieren Sie Unterschiede - wenn sich Implementierungen absichtlich unterscheiden, dokumentieren Sie warum
- Erhalten Sie die Funktionalität - stellen Sie sicher, dass die konsolidierte Version alle Anwendungsfälle abdeckt
Schritt 7: Kommentare bereinigen
Nach dem Refactoring entfernen Sie unnötige Kommentare:
- Entfernen Sie Migrationsnotizen - Kommentare, die erklären, woher der Code verschoben wurde (z. B. "isCollecting is now exported from Table/utils.ts")
- Entfernen Sie offensichtliche Kommentare - Kommentare, die nur wiederholen, was der Code tut (z. B. "// Re-export shared aggregations" wenn der Code eindeutig Re-Exports zeigt)
- Behalten Sie erklärende Kommentare - Kommentare, die erklären, WARUM etwas getan wird (z. B. "// Use setTimeout to ensure form state is updated")
- Behalten Sie Kommentare zu komplexer Logik - Kommentare, die nicht offensichtliche Geschäftslogik oder Randfälle erklären
- Entfernen Sie TODO/FIXME-Kommentare - es sei denn, es handelt sich um legitime zukünftige Arbeiten
Beispiele für unnötige Kommentare:
// ❌ Unnötig - offensichtlich aus dem Code
// Re-export shared aggregations from util
export { latest, totalRange } from "@/util/keywords/gridAggregations";
// ❌ Unnötig - Migrationsnotiz, nicht mehr benötigt
// isCollecting is now exported from Table/utils.ts to avoid duplication
// Import it from there: import { isCollecting } from "../KeywordListDetail/Table/utils";
Beispiele für gute Kommentare:
// ✅ Gut - erklärt WARUM
// Use setTimeout to ensure form state is updated after CSV processing
setTimeout(() => {
void handleSubmit(onSubmit)();
}, 100);
// ✅ Gut - erklärt komplexe Logik
// A keyword is updated if:
// 1. Its lastUpdated value changed (most reliable indicator), OR
// 2. It's no longer in collecting state AND we've polled enough times (fallback)
Schritt 8: Verbesserung der Codequalität
Wenden Sie konsistente Codequalitätsmuster im gesamten refaktorierten Code an:
Null/Undefined-Prüfungen
Vereinfachen Sie Null- und Undefined-Prüfungen mit loser Gleichheit:
// ❌ Ausführlich
if (value !== undefined && value !== null) { ... }
if (value !== undefined && value !== null && value !== "") { ... }
// ✅ Vereinfacht
if (value != null) { ... }
if (value != null && value !== "") { ... }
Hinweis: Verwenden Sie != null (lose Gleichheit), um sowohl auf null als auch auf undefined in einer einzigen Prüfung zu testen. Dies ist idiomatisches TypeScript/JavaScript und prägnanter.
Typ-Only-Imports
Verwenden Sie import type für typenreine Imports, um die Build-Leistung zu verbessern und die Absicht klar zu machen:
// ❌ Runtime-Import für typenreine Verwendung
import { SomeType } from "app-api/types/domain";
// ✅ Typ-Only-Import
import type { SomeType } from "app-api/types/domain";
Anwenden auf:
- Typdefinitionen aus
app-api - Interface-Imports
- Typenreine Hilfstypen
- Jeden Import, der nur in Typannotationen verwendet wird
Entfernen unnötiger Typ-Assertions
Entfernen Sie Typ-Assertions, wenn TypeScript den Typ korrekt ableiten kann:
// ❌ Unnötige Assertion
const items = data.map((item) => item as SomeType);
// ✅ TypeScript ableiten lassen
const items = data.map((item) => item);
Wann Assertions behalten werden sollten: Nur wenn TypeScript den Typ nicht wirklich ableiten kann oder wenn Typen auf eine Weise eingeengt werden, die TypeScript nicht überprüfen kann.
Entfernen unnötiger Nullish Coalescing
Entfernen Sie ?? null, wenn der Wert bereits nullable ist oder TypeScript den korrekten Typ ableitet:
// ❌ Unnötig - valueGetter gibt bereits nullable zurück
valueGetter: (value, row) => {
return row.someField ?? null;
};
// ✅ Vereinfacht
valueGetter: (value, row) => {
return row.someField;
};
Wann behalten werden sollte: Wenn explizit undefined in null für die API-Kompatibilität umgewandelt wird oder wenn das Typsystem es erfordert.
Entfernen ungenutzter Props
Entfernen Sie Props aus Komponenten-Schnittstellen und Implementierungen, wenn sie nicht verwendet werden:
// ❌ Ungenutztes Prop
interface ComponentProps {
item: Item;
unusedField: string; // Wird nie in der Komponente verwendet
}
// ✅ Saubere Schnittstelle
interface ComponentProps {
item: Item;
}
Explizite Typen zu Callbacks hinzufügen
Fügen Sie explizite Typannotationen zu Callback-Parametern hinzu, wenn TypeScript sie nicht ableiten kann:
// ❌ Implizites any
const items = ids.map((id) => { ... });
// ✅ Expliziter Typ
const items = ids.map((id: string | number) => { ... });
Anwenden auf: Array-Methoden (map, filter, forEach), Event-Handler und andere Callbacks, bei denen Typen nicht aus dem Kontext klar sind.
Wiederholte JSX-Muster extrahieren
Extrahieren Sie wiederholte JSX-Muster in wiederverwendbare Komponenten:
// ❌ Wiederholtes Muster
{
isAggregation ? (
<Typography
variant="body2"
fontWeight="medium"
fontStyle="italic"
color="text.secondary"
>
{formattedValue}
</Typography>
) : (
<Typography variant="body2" fontWeight="medium">
{formattedValue}
</Typography>
);
}
// ✅ Extrahierte Komponente
<AggregationTypography
formattedValue={formattedValue}
isAggregation={isAggregation}
/>;
Factory-Funktionen für variierte Implementierungen verwenden
Wenn die Logik ähnlich ist, aber mit unterschiedlichen Datenstrukturen umgehen muss, verwenden Sie Factory-Funktionen:
// ✅ Factory-Funktionsmuster
export const createIntentOperators = <T extends { intent?: Intent | Intent[] }>(
normalizeToArray: (value: T) => Intent[]
) => {
return {
// Operatoren, die mit normalisiertem Array arbeiten
};
};
// Verwendung in verschiedenen Features
const operators1 = createIntentOperators<Feature1Type>((item) => [item.intent]);
const operators2 = createIntentOperators<Feature2Type>(
(item) => item.intents || []
);
Vorteile: Wiederverwendung der Kernlogik bei gleichzeitiger angemessener Handhabung verschiedener Datenstrukturen.
Code-Review-Härtung (wiederkehrendes Feedback)
Wenden Sie dies an, wenn das Refactoring Grids, Dashboards, Abrufe oder gemeinsame "Zeilen- + Metrik-"Formen berührt (entspricht wiederholtem Moz Pro UI PR-Feedback).
DataGrid: Detail-Panel-Umschalter
- Bevorzugen Sie
DetailPanelToggleauspattern-libraryzum Auf-/Zuklappen einer Detail-Panel-Spalte anstelle eines ad-hocIconButton+@mui/iconsin einer benutzerdefinierten Zelle. - Accessibility verdrahten: übergeben Sie
buttonTabIndex={0}, wenn die Zelle tab-fokussierbar sein soll (sieheapp/(default)/competitive-research/top-competing-content/_module/components/Table/columns.tsx,SerpFeaturesCell). - Layout / Zentrierung: Verwenden Sie
sxaufBox/Stack(oderStacksdirection/spacing, wenn dies das etablierte Muster ist). Ersetzen Sie nichtsxdurch veraltete MUI System-Style-Props aufBox(z. B.display,alignItemsals Top-Level-Props) – MUI veraltet diesen Ansatz zugunsten vonsx.
Typisierung: object / anonyme Zeilenformen vermeiden
- Verwenden Sie nicht
readonly object[]fürbrand_definitions, Zeilenfragmente oder API-nahe Listen, wenn ein struktureller Typ existiert oder einmal definiert werden kann. - Definieren Sie eine gemeinsame "like"-Schnittstelle (z. B.
BrandDefinitionLike) im Feature-utils-Modul; exportieren Sie sie und einen einzigen display-line-Helfer (z. B.getBrandDefinitionDisplayLine), anstattgetBrandNamemit Casts in Zellen zu duplizieren. - Entfernen Sie redundante Casts (
as BrandDefinitionLike[]), nachdem Parameter typisiert sind. - Wiederverwenden Sie Domain-Helfer aus
*_module/utils/dataHelpers, wenn die Form eine echteBrandDefinitionist; verwenden Sie den lockereren Helfer nur, wo sich Prompt-Zeilen-Payloads unterscheiden.
Typography vs. Box (pattern-library)
- Für Text (Leerzustände, Inline-"—", hervorgehobene Spans) bevorzugen Sie
Typographyauspattern-librarymitcomponent="span", wenn angemessen – nichtBox, das sich als Text ausgibt. Typography-API: Verwenden Sievariant,color,fontWeight,component, wo sie Semantik ausdrücken (diese sind nicht veraltet).Box/ Layout-Wrapper: Bevorzugen Siesxfür Abstände, Flex-Layout, Größen- und Theme-Werte. Vermeiden Sie beiläufige Refactorings, die Styling vonsxzuBox"style"-Kurzschreibweisen verschieben; dies widerspricht der aktuellen MUI-Empfehlung.
Komponentengrenzen: Props vs. interner Zustand
- Leiten Sie Booleans aus
dataab innerhalb der Komponente, wenn das übergeordnete Element sie nicht für Layout oder URL-Status benötigt (z. B. "hat Markenbegriffe" aus denbrand_termsder Primärmarke). - Halten Sie UI-only-Toggles intern (z. B. Umschalten zwischen zwei Diagrammmodi in einer Karte), es sei denn, ein anderes Geschwister oder eine Route muss denselben Status steuern – dann dokumentieren Sie, warum das Anheben erforderlich ist.
Kontrollfluss und JSX
- Bevorzugen Sie eine frühe
return-Anweisung für Lade-/Leerzweige anstelle eines großen ternären Operators, der einer Variablen unmittelbar vor einem einzelnenreturnzugewiesen wird, es sei denn, die Memoisierung erfordert wirklich einen Ausdruck. - Sortiervergleicher: Härten Sie niemals eine magische Sentinel-Zahl (z. B.
5) ein, die an ein angenommenes Domänenmaximum gebunden ist; verwenden SieNumber.POSITIVE_INFINITY/ explizites "sort last" für null/undefined, damit das Verhalten korrekt bleibt, falls sich Limits ändern.
Abrufe und Paginierung
- Vermeiden Sie ein festes kleines
limit(z. B. 100) für Dashboard-Listen, wenn das Backend paginiert ist und Projekte eine Seite überschreiten können. - Durchlaufen Sie mit einer stabilen Seitengröße, bis eine Seite weniger Zeilen als das Limit oder null Zeilen zurückgibt, oder folgen Sie dem dokumentierten Vertrag der Aktion (siehe
moz-pro-service-Aktionsdokumentation / Bruno für maxlimit). - Geben Sie
totaloder abgeleitete Summen konsistent an, wenn Aufrufer Zählungen benötigen.
Präsentations-Wrapper: nicht das Offensichtliche wiederholen
- Entfernen Sie redundante Alt-Status-Felder, wenn die übergeordnete
Cardbereits den sichtbaren Titel setzt (z. B. vermeiden Sie die Duplizierung voncardContentTitle, das mit dem Kartentitel übereinstimmt). - Null-Prüfungen: Wenn ein Prop als
stringtypisiert ist, führen Sie keine Prüfung aufnulldurch, es sei denn, es gibt einen Laufzeitpfad, der null setzt (selten in typisiertem React-Code). - Props sortieren: Halten Sie Props in JSX in einer konsistenten Reihenfolge (erforderliche Props zuerst, gefolgt von optionalen Props, dann Event-Handler).
Datenabruf-Zustandsmaschine
- Verwenden Sie eine Zustandsmaschine mit drei Zuständen für jeden Datenabruf:
loading,error,success. Vermeiden Sie es, Fehler als impliziten "leeren Zustand" zu behandeln. - Behandeln Sie Fehler explizit mit einer UI-Fehlermeldung oder einem erneuten Abruf-Button (siehe
app/(default)/competitive-research/_module/components/ErrorState.tsx). - Zeigen Sie leere Zustände nur an, wenn der Abruf erfolgreich war, aber keine Daten zurückgegeben wurden. Verwechseln Sie dies nicht mit Lade- oder Fehlerzuständen.
Fazit
Diese Refactoring-Skill bietet einen umfassenden Rahmen zum systematischen Verbessern der Codequalität, Reduzieren von Duplikaten und Sicherstellen der Konsistenz in einer großen React/TypeScript-Codebase. Durch Befolgen dieser Schritte können Entwickler wartbareren, wiederverwendbareren und robusteren Code erstellen.
Schlüsselerkenntnisse:
- Analysieren Sie vor dem Refactoring – identifizieren Sie ähnliche Features und Duplikate, bevor Sie Änderungen vornehmen.
- Extrahieren Sie in gemeinsame Speicherorte – Konsolidieren Sie Code in
@/components/,@/util/oderpattern-library. - Gleichen Sie Muster an – stellen Sie sicher, dass Features denselben Strukturen, Konventionen und Datenflüssen folgen.
- Bereinigen Sie nach dem Refactoring – entfernen Sie unnötige Kommentare und wenden Sie Verbesserungen der Codequalität an.
- Befolgen Sie Code-Review-Best-Practices – wenden Sie konsistente Muster für DataGrid, Typisierung, Styling und Datenabruf an.