2026

16. Juni 2026

Igitt, das stinkt! react-stinky ist da

Ein Code Smell ist das React-Äquivalent zu Milch, die übers Wochenende gekippt ist: Sie gießt sich noch, aber irgendwas stimmt nicht. react-stinky ist ein Agent Skill, der durch deine komplette Komponente, deinen Hook oder dein Modul geht, die Kosten jedes Smells benennt, einen konkreten Fix mit Quelle vorschlägt und (der Teil, der ihn brauchbar macht) weiß, wann er den Mund halten soll. Hier ist, wonach er schnüffelt, wie er den Mief bewertet und warum die Liste der Dinge, die er nicht meldet, wichtiger ist als die Liste der Dinge, die er meldet.

S
Sascha Becker
Author

10 Min. Lesezeit

Igitt, das stinkt! react-stinky ist da

Igitt, das stinkt! react-stinky ist da

HABEN SIE ES SATT, eine Komponente zu öffnen und vom Mief umgehauen zu werden? Hat Ihr <Stack> ein onClick, das kein Tastaturnutzer je erreicht? Vermehren sich Ihre Boolean-Props wie die Karnickel, isSmall neben isLarge neben isError neben isLoading, bis keiner mehr weiß, welche Kombination überhaupt erlaubt ist? Leute, das kennen wir ALLE. Am Freitag ging der Code sauber raus. Am Montag STINKT er. Aber was, wenn es einen Weg gäbe, den Mief aufzuspüren, bevor es Ihr Reviewer tut? Hier kommt react-stinky, der Code-Smell-Detektor, der durch Ihre ganze Komponente läuft, sich die Nase zuhält und Ihnen genau sagt, was da gestorben ist und wie man es beerdigt. Er schneidet. Er hackt. Er zitiert die React-Docs. Aber Moment, es kommt noch besser.

Okay. Teleshopping-Stimme aus.

react-stinky ist ein neuer Agent Skill in meinem offenen Set, und unter dem Dauerwerbesendungs-Lack steckt ein ernsthaftes Werkzeug, gebaut um eine Bedingung: Ein Smell-Detektor ist nur so gut wie die Dinge, über die er schweigt. Hier ist, was er tut, was er sich weigert zu tun, und warum diese zweite Liste der Teil ist, der ihn behaltenswert macht.

Wonach er wirklich schnüffelt

react-stinky ist ein ganzheitlicher Code-Smell-Detektor für React und TypeScript. Das Wort, das sich hier verdient, ist ganzheitlich. Eine normale Lint-Regel betrachtet eine Zeile isoliert. react-stinky liest die ganze Komponente, den Hook, das Modul, und stellt die Fragen, die ein gründlicher Reviewer stellt. Lässt sich dieser State aus Props ableiten, statt ihn zu speichern? Macht dieser Effect einen Job, der in einen Event-Handler gehört? Kann ein Tastaturnutzer das hier überhaupt auslösen? Erlaubt diese Prop-Form einen State, der unmöglich sein sollte?

Für jeden Smell, den er findet, tut er drei Dinge, die ein Linter normalerweise nicht tut:

  1. Er benennt die Kosten. Nicht "vermeide das", sondern "ein zusätzlicher Render und ein State-Wert, der von seinen Inputs abdriften kann". Du bekommst den echten Preis und entscheidest selbst, ob er ihn wert ist.
  2. Er schlägt einen konkreten Fix vor, vorher und nachher, in der Sprache der Datei.
  3. Er verlinkt die Quelle. React-Docs, MDN, das TypeScript-Handbuch, MUI. Jedes Finding ist ein Argument mit Beleg, keine Geschmacksfrage.

Die acht Säulen

Die Abdeckung umfasst acht Säulen. Sieben davon arbeiten auf einer einzelnen Datei:

  1. Komponenten-API und Props, das Rückgrat. Benennung, Boolean- und Callback-Konventionen, String-Unions statt Boolean-Flags, Discriminated Unions, die unmögliche States verbieten, controlled und uncontrolled State, Slots und Children-Komposition, Generics, Refs, Styling-APIs, Accessibility-Props, Server-Component-Grenzen, JSDoc.
  2. State und Datenfluss. Ableitbare Werte in useState, Props, die in State kopiert werden und dann abdriften, dieselbe Tatsache an zwei Stellen gespeichert, Prop Drilling durch Schichten, die sie nur weiterreichen.
  3. Effects und Lifecycle. Effects, die abgeleitete Daten berechnen, Fetches und Subscriptions und Timer ohne Cleanup (Races und Leaks), Dependency-Arrays, die lügen über das, was der Effect liest.
  4. Komponentenstruktur und Hooks. Gott-Komponenten, die Fetching, Logik und Darstellung auf einmal machen; eine Komponente, die in einer anderen definiert ist (bei jedem Render ein brandneuer Typ); konditionale Hooks.
  5. Render-Korrektheit. Array-Index als key auf einer Liste, die umsortiert oder editiert; direkte Mutation von State oder Props; verschachtelte Ternary-Suppe im JSX; kopierte Blöcke, die einen parametrisierten Helper wollen.
  6. Accessibility im Markup. onClick auf einem <div> ohne Rolle, ohne tabIndex und ohne Tastatur-Handler; Div-Suppe, wo semantische Elemente hingehören; Formularfelder ohne zugeordnetes Label.
  7. TypeScript-Disziplin. any und as any und @ts-ignore, lügende as-Casts, Non-Null-! auf einem Wert, der null sein kann.

Die achte Säule, dateiübergreifende Duplikation, läuft nur im Ordner- und Repo-Scope, weil sie Dateien gegeneinander vergleicht: eine wiederverwendbare Komponente, die woanders inline nachgebaut ist, ein Hook, der kopiert statt geteilt wird, ein Typ, der an zwei Stellen deklariert ist und leise auseinanderdriftet.

Wie er den Mief bewertet

Nicht jeder Smell ist ein Notfall, und ein Werkzeug, das alle gleich behandelt, bringt dir bei, es zu ignorieren. react-stinky sortiert jedes Finding in drei Stufen.

Die Stufe ist der Punkt. Sie ist der Unterschied zwischen einem Report, den du von oben bis unten abarbeitest, und einem, den du überfliegst und schließt.

Der Teil, der ihn brauchbar macht

Das ist die These, also bekommt sie ihren eigenen Abschnitt. Jeder kann einen Checker schreiben, der jedes onClick, jedes any und jeden Index-Key meldet. Zurück bekommst du eine Wand aus Lärm, die du wegzuscrollen lernst, und das ist schlimmer als gar kein Werkzeug, weil die echten Probleme jetzt unter den falschen begraben liegen.

react-stinky trägt eine Schutzleine. Jeder Smell im Katalog hat eine "Nicht melden"-Zeile, und eine Handvoll Regeln zieht sich durch alle.

  • Native HTML-Attribute bleiben nackt. Er sagt dir nicht, du sollst disabled in isDisabled umbenennen oder onChange an einem echten <input> in onValueChange.
  • Etablierte Library-Konventionen sind keine Smells. MUIs open, slots und sx; Radix' asChild. Spricht die Datei diesen Dialekt schon, spricht react-stinky ihn zurück, statt ihn zu korrigieren.
  • Config-Objekt-Props sind richtig für datengetriebene Komponenten mit fester Anordnung, etwa ein Data Grid. Nicht jedes Config-Array will Compound Components werden.
  • Ein Effect ist das richtige Werkzeug für echte externe Synchronisation. Er meldet den Effect, der abgeleiteten State berechnet, nicht den, der mit einem Websocket, localStorage oder einem Nicht-React-Widget spricht.
  • Index-Keys sind in Ordnung auf einer statischen Liste, die nie umsortiert, einfügt oder löscht. Der Bug taucht erst auf, wenn sich Items bewegen.
  • Ein Finding pro echtem Problem, und der kleinste Fix, der den Smell entfernt. Eine konsistente lokale Konvention schlägt den Katalog-Standard.

Was er bewusst nicht anfasst

react-stinky hat Nachbarn, und er bleibt aus deren Gärten. Zwei Themen reicht er absichtlich weiter:

Sind diese Skills nicht installiert, notiert react-stinky das Finding in einer Zeile und geht weiter. Er baut sie nicht nach, und er bläht seinen Report nicht mit einer Kategorie auf, die ein anderes Werkzeug besitzt. Ein Skill, der alles versucht, kann nichts richtig, also zieht dieser eine harte Kante um React- und TypeScript-Wartbarkeit und hört dort auf.

Passe den Scope an die Frage an

Du richtest react-stinky auf so viel oder so wenig, wie du willst, und er passt die Arbeit an die Frage an.

ScopeAuslöserWas er liest
Fragmenteine eingefügte Funktionnur diese Fläche, mit Angabe, was er über alles Off-Screen annahm
Dateieine oder mehrere genannte Dateienjede Datei vollständig, jede Komponente, jeden Hook, jedes Prop-Interface
Ordnerein Verzeichnisden Ordner, plus den dateiübergreifenden Duplikations-Durchlauf
Repo-Sweep"smell-check die Codebasis"die Komponenten, Hooks und Module, mit Vorrang für geteilten und exportierten Code

Eine Ehrlichkeitsregel ist eingebaut. Im Einzeldatei- oder Fragment-Scope, wo er andere Dateien nicht sehen kann, sagt er dir, dass dateiübergreifende Duplikation nicht geprüft wurde, statt zu suggerieren, der Code sei einzigartig. Das Werkzeug sagt, was es nicht angeschaut hat, nicht nur, was es gefunden hat.

Wie ein Report aussieht

Lass ihn laufen, und du bekommst Findings nach Datei sortiert, jedes mit Stufe, Ort, Kosten, einem Vorher-Nachher-Fix und einer Quelle.

text
React Stinky report, src/components/SeedRow.tsx
[Rancid] clickable-nonsemantic (a11y markup), Zeile 297
Smell: ein <Stack> (rendert ein div) hat onClick, aber keine role, tabIndex oder Tastatur-Handler.
Kosten: Tastatur- und Screenreader-Nutzer können es nicht auslösen; für assistive Technik unsichtbar.
Fix: ein echtes Control rendern (component="button" oder eine IconButton), oder
role="button" tabIndex={0} und ein onKeyDown für Enter und Space ergänzen.
Quelle: MDN button role
[Funky] effect-for-derived (state and effects), Zeile 40
Smell: ein useEffect plus setState berechnet `fullName` aus `first` und `last`.
Kosten: ein zusätzlicher Render und ein State-Wert, der von seinen Inputs abdriften kann.
Fix: beim Render berechnen, const fullName = `${first} ${last}`. Effect und State löschen.
Quelle: React, You Might Not Need an Effect
Summary: 1 rancid, 1 funky across 1 file.

Übersteht nichts die Schutzleine, sagt er es klar: "Smells fresh. No maintainability smells found." Ein sauberes Gutachten ist ein Ergebnis, kein Versagen bei der Arbeitssuche.

Woher die Regeln kommen

Die Regeln, die react-stinky durchsetzt, sind nicht für den Anlass erfunden. Der Großteil von Säule 1, dem Komponenten-API-Rückgrat, kommt direkt aus Can't Maintain, einem Spiel, das ich gebaut habe und das den Blick für langlebige React-APIs schult. Es stellt zwei Versionen derselben Komponente nebeneinander, du wählst die, die besser altert, und es sagt dir warum, mit Link zur React-, TypeScript-, MDN- oder MUI-Quelle, die den Fall belegt.

react-stinky ist die andere Hälfte dieser Idee. Das Spiel bringt einem Menschen bei, einen Smell nach dem anderen zu erkennen. Der Skill nimmt denselben Katalog und lässt ihn auf einmal über eine ganze Datei oder ein ganzes Repo laufen, sodass du nicht jeden einzeln von Hand findest. Das eine schult das Auge, das andere macht den Durchlauf, und beide zitieren dieselben Quellen, damit der Fall nie bloß Geschmack ist.

Aus Can't Maintain ist inzwischen Cant geworden, ein Hub aus Vergleichsspielen, der über React hinaus inzwischen TypeScript, Git, SEO, Testing und UX erreicht. Wenn ein Smell-Report dir Lust macht, diese Dinge schneller zu fangen, bevor sie rausgehen, ist das Spiel der Ort zum Üben. Es gibt mehr über Cant auf der Projektseite.

Probier ihn aus

react-stinky installiert in jeden Agenten, der das skills.sh-Format spricht (Claude Code, Cursor, Codex, Cline, Windsurf, OpenCode):

bash
npx skills@latest add saschb2b/skills --skill react-stinky

Dann richte ihn auf eine Komponente, einen Ordner oder das ganze Repo und bitte ihn zu schnüffeln.

Quellen


S
Geschrieben von
Sascha Becker
Weitere Artikel