Web razvoj
ReactPregled kodaPerformansePristupačnostTestiranjeTypeScript

Kontrolna lista za React code review koju koristimo: performanse, pristupačnost i održivost

AO
Adrijan Omićević
·14 min čitanja

# Uvod#

React PR može izgledati uredno, a svejedno isporučiti regresije: komponenta se rerenderira na svaki pritisak tipke, modal pogrešno zarobi fokus ili hook potajno stvori zastarjelo stanje. Cijena se pokaže kasnije kroz spore stranice, pritužbe korisnika i krhak kod koji nitko ne želi dirati.

Ovo je kontrolna lista za React code review koju koristimo u Samiodi za produkcijske aplikacije. Strukturirana je za stvarne PR-ove i uključuje primjere za komponente, hookove, stanje, renderiranje, pristupačnost i testiranje, uz copy-paste predložak za GitHub ili Linear te smjernice što automatizirati lint pravilima.

Za dublje urone, držite pri ruci i ovo tijekom reviewa: našu kontrolnu listu za pristupačnost u Reactu, naš vodič za profiliranje performansi u Reactu i našu kontrolnu listu za sigurnost web aplikacija.

# Kako koristiti ovu kontrolnu listu u stvarnim PR-ovima#

Kontrolna lista vrijedi samo ako smanjuje rizik bez usporavanja isporuke. Naš pristup je jednostavan: automatizirajte ono što se može automatizirati i sačuvajte ljudski review za stvari koje zahtijevaju prosudbu.

Workflow za review koji skalira#

  1. 1
    Prvo pokrenite automatizaciju: formatiranje, lint, typecheck, unit testove i osnovni a11y linting treba vrtjeti u CI-ju i lokalno.
  2. 2
    Brzo skenirajte diff: identificirajte što se promijenilo i što bi moglo biti pogođeno. Mala UI promjena i dalje može utjecati na renderiranje, upravljanje fokusom ili rukovanje greškama.
  3. 3
    Pregledajte po područjima rizika: performanse, a11y, održivost i testovi. Sekcije ispod mapiraju se direktno na to.
  4. 4
    Tražite izmjene uz razlog: reviewer treba objasniti utjecaj na korisnika, a ne osobne preferencije stila.

Što automatizirati, a što ručno pregledati#

KategorijaAutomatizirati alatimaRučno pregledati
FormatiranjePrettierNišta
Type safetyTypeScript strict mode, tsc --noEmitDizajn API-ja, modeliranje domene
Kvaliteta kodaESLint, React Hooks lint, pravila za importImenovanje, razdvajanje odgovornosti
Pristupačnosteslint-plugin-jsx-a11y, Storybook a11yTokovi tipkovnicom, focus trapovi, složeni widgeti
PerformanseBuild upozorenja, bundle analyzerOkidači rerendera, granice memoizacije, velike liste
TestiranjeJest, React Testing Library, coverage gateoviKvaliteta testova, realni korisnički scenariji
SigurnostSkeniranje dependencyja, skeniranje tajniAutorizacijska logika, izloženost podataka

ℹ️ Napomena: Statički alati uhvate puno toga, ali ne mogu u potpunosti validirati UX tipkovnicom, stvarni trošak rerendera ili pokrivaju li testovi smisleno ponašanje. Cilj je manje rasprava oko osnova i više pažnje na rizike za proizvod.

# Kontrolna lista, dio 1: Komponente i dizajn API-ja#

Dobre React komponente su predvidljive, lako se ponovno koriste i teško ih je pogrešno koristiti. Većina dugoročnih problema s održavanjem počinje nejasnim odgovornostima komponenti i nestabilnim API-jima.

Odgovornosti komponenti i granice#

Provjeri

  • Komponenta radi jednu stvar. Ako dohvaća podatke, transformira ih i renderira više nepovezanih sekcija, kandidat je za refaktor.
  • Logika kontejnera i prezentacijski UI su razdvojeni kad kompleksnost naraste.
  • Zajednička logika se izvlači u hookove ili utilse, a ne duplicira.

Primjer: razdvoji podatke i UI kad ponašanje naraste

TSX
// Good: UI stays testable and reusable
type UserCardProps = {
  user: { id: string; name: string; email: string };
  onEdit: (id: string) => void;
};
 
export function UserCard({ user, onEdit }: UserCardProps) {
  return (
    <section aria-label="User">
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      <button type="button" onClick={() => onEdit(user.id)}>
        Edit
      </button>
    </section>
  );
}

Props, defaulti i tipovi#

Provjeri

  • Tipovi propsa su eksplicitni i minimalni. Izbjegavajte “bag of props” pristup.
  • Optional propsi imaju jasne default vrijednosti, idealno na razini parametara.
  • Nazivi callback propsa predstavljaju namjeru, ne detalje implementacije.

Čest “smell”

  • Komponenta prima data: any i setData: any i ponaša se kao mini-aplikacija.

Bolje

  • Proslijediti samo podatke koji su potrebni i callbackove temeljene na namjeri poput onSubmit ili onSelect.

Greške, loading i empty stanja#

Provjeri

  • Stanja greške i učitavanja su obrađena na pravoj razini.
  • Empty stanja su namjerna i pristupačna.
  • UI se nepotrebno ne “bljeska” između stanja.

Konkretno pitanje za review

  • Ako API vrati prazan niz, što korisnik vidi i može li se oporaviti?

Kompozicija i varijante#

Provjeri

  • Varijante koriste kompoziciju umjesto kaosa s uvjetima.
  • Ako komponenta ima više od 3 do 4 propsa za varijante, razmislite o razdvajanju u podkomponente.

Primjer: izbjegnite eksploziju propsa

  • Umjesto variant="primary" size="sm" iconLeft="..." iconRight="..." loading, razmotrite Button plus Button.Icon plus Button.Loading pattern gdje je prikladno.

# Kontrolna lista, dio 2: Hookovi i side effecti#

Bugovi u hookovima su skupi jer često prođu ručni QA i padnu tek u edge caseovima s tajmingom ili navigacijom.

Ispravnost React Hookova#

Provjeri

  • Ovisnosti u useEffect su ispravne, bez “nedostaje, ali radi”.
  • Effecti se koriste za sinkronizaciju, ne za izvođenje UI stanja.
  • Postoje cleanup funkcije za subscriptione, timere i event listenere.

Loše: effect se vrti prečesto i pokreće dodatno stanje

TSX
useEffect(() => {
  setFormValue(user.name);
}, [user]);

Bolje: ovisite o stvarnoj vrijednosti

TSX
useEffect(() => {
  setFormValue(user.name);
}, [user.name]);

Stabilni callbackovi i izvedene vrijednosti#

Provjeri

  • useCallback i useMemo se koriste kad sprječavaju mjerljive rerendere ili skupi rad, ne kao default.
  • Izvedeni podaci se računaju iz izvornog stanja, ne spremaju se kao dodatno stanje.

“Smell”

  • const [filtered, setFiltered] = useState(...) gdje je filtered uvijek izveden iz items i query.

Bolje

  • Izračunati filtered uz memoizaciju samo ako je skupo.
TSX
const filtered = useMemo(() => {
  return items.filter((i) => i.name.toLowerCase().includes(query.toLowerCase()));
}, [items, query]);

API custom hookova#

Provjeri

  • Custom hookovi izlažu mali, koherentan shape povratne vrijednosti.
  • Async hookovi imaju cancellation ili ignoriraju zastarjele odgovore.
  • Error stanje je eksplicitno.

Primjer: ignoriraj zastarjele odgovore

TSX
useEffect(() => {
  let active = true;
 
  (async () => {
    const result = await fetchUser(userId);
    if (active) setUser(result);
  })();
 
  return () => {
    active = false;
  };
}, [userId]);

⚠️ Upozorenje: Korištenje useEffect za async posao bez zaštite od zastarjelih odgovora može prikazati pogrešne podatke nakon brze navigacije ili brzih promjena unosa.

# Kontrolna lista, dio 3: Upravljanje stanjem i tok podataka#

Većina React kompleksnosti je samonametnuta zbog nejasnog vlasništva nad stanjem. Review treba razjasniti što je lokalno stanje, što je server-state i što je izvedeno.

Vlasništvo nad stanjem i colocating statea#

Provjeri

  • Stanje živi tamo gdje se koristi. “Liftajte” state gore samo kad je nužno.
  • Izbjegavajte “global state by default”. Globalno stanje čini ovisnosti implicitnima.
  • Koristite alate za server-state za server data pattern gdje ima smisla.

Konkretno pitanje za review

  • Može li se ovo stanje izvesti iz propsa ili URL parametara umjesto da se sprema?

Izbjegavanje dupliciranog i nekonzistentnog stanja#

Provjeri

  • Nema dupliciranih izvora istine poput isOpen i selectedId koji mogu “odlutati”.
  • Kompleksni formovi koriste konzistentan pristup, ne ad-hoc stanje po polju.

Primjer “smell”

  • I items i itemsCount se spremaju odvojeno.

Reduceri za višekoračne interakcije#

Provjeri

  • Kad tranzicije stanja nisu trivijalne, razmotrite useReducer ili state machine.
  • Nazivi akcija odražavaju korisničku namjeru.

Primjer: reducer pojašnjava tranzicije

TSX
type State = { status: "idle" | "saving" | "error" };
type Action = { type: "save" } | { type: "success" } | { type: "fail" };
 
function reducer(state: State, action: Action): State {
  switch (action.type) {
    case "save":
      return { status: "saving" };
    case "success":
      return { status: "idle" };
    case "fail":
      return { status: "error" };
    default:
      return state;
  }
}

# Kontrolna lista, dio 4: Renderiranje i performanse#

Problemi s performansama obično dolaze od nepotrebnih rerendera, skupog rada unutar rendera i neograničenih listi. Moderni React može biti brz, ali je također lako slučajno poništiti optimizacije.

Za detaljniji workflow s React DevTools Profilerom i patternima memoizacije, koristite naš vodič o profiliranju performansi u Reactu i patternima renderiranja.

Sprječavanje nepotrebnih rerendera#

Provjeri

  • Props proslijeđeni memoiziranoj djeci su stabilni kad je to potrebno.
  • Inline object i array literali se ne prosljeđuju dubokim stablima bez razloga.
  • Event handleri su stabilni kad je bitno.

Loše: nestabilan prop uzrokuje rerendere djeteta

TSX
<Chart options={{ showLegend: true }} data={data} />

Bolje: memoizirajte stabilne objekte

TSX
const chartOptions = useMemo(() => ({ showLegend: true }), []);
<Chart options={chartOptions} data={data} />

Renderiranje lista i key-evi#

Provjeri

  • Liste koriste stabilne key-eve iz podataka, ne indexe niza.
  • Vrlo velike liste koriste virtualizaciju.
  • Rad renderiranja skalira s veličinom podataka.

Konkretan benchmark

  • Kad se približite stotinama DOM redaka, scrollanje i rerenderi postaju vidljivo lošiji na uređajima srednje klase. Virtualizacija se često isplati na 500 do 1.000 stavki, ovisno o kompleksnosti retka.

Uvjetno renderiranje i layout shiftovi#

Provjeri

  • UI izbjegava skokove layouta gdje je moguće. Rezervirajte prostor za loadere i slike.
  • Skeletoni ili placeholderi ne kvare navigaciju tipkovnicom.

Izbjegnite skupe izračune u renderu#

Provjeri

  • Sortiranje, grupiranje i formatiranje se miče iz rendera ili memoizira.
  • Formatiranje datuma i brojeva u velikim tablicama se cacheira ili unaprijed izračuna.

💡 Savjet: Ako se komponenta renderira više puta po jednoj korisničkoj interakciji, profilirajte prije dodavanja memoizacije. U mnogim aplikacijama najveći dobitak dolazi od uklanjanja redundantnog stanja i popravljanja ovisnosti effecta, a ne od omatanja svega u memo.

# Kontrolna lista, dio 5: Pristupačnost i UX osnove#

Pristupačnost nije zadatak “za kasnije”. Review je najbolje mjesto za hvatanje problema prije nego postanu patterni koji se ponavljaju kroz cijeli codebase.

Za detaljnije ARIA i fokus smjernice koristite našu a11y kontrolnu listu za React pristupačnost.

Prvo semantički HTML#

Provjeri

  • Koristite semantičke elemente prema namjeri: button za akcije, a za navigaciju, naslove pravilnim redoslijedom.
  • Izbjegavajte klikabilne div ili span. Ako baš mora postojati, trebaju keyboard handleri i role, ali to je i dalje drugo najbolje rješenje.

Primjer: ispravna upotreba buttona

TSX
<button type="button" onClick={onDelete} aria-label="Delete user">
  Delete
</button>

Pristupačna imena i labele#

Provjeri

  • Inputi imaju labele povezane preko htmlFor i id.
  • Buttoni samo s ikonom imaju aria-label.
  • aria-describedby se koristi za hint i error tekst.

Provjeri

  • Modali zarobe fokus i vrate fokus pri zatvaranju.
  • Dropdowni i meniji rade tipkovnicom, uključujući Escape za zatvaranje.
  • Fokus je vidljiv i nije uklonjen.

Konkretno pitanje za review

  • Možete li završiti cijeli flow koristeći samo Tab, Shift+Tab, Enter, Space i Escape?

ARIA upotreba#

Provjeri

  • ARIA se koristi samo kad semantički HTML ne može izraziti ponašanje.
  • Nema konfliktnih atributa poput aria-hidden na fokusabilnim elementima.

Motion i reduced motion#

Provjeri

  • Animacije poštuju korisničke preferencije gdje je relevantno.
  • UI s puno tranzicija ne blokira interakcije.

# Kontrolna lista, dio 6: Testiranje i QA signali#

Testovi trebaju štititi ponašanje do kojeg je korisnicima stalo, ne detalje implementacije. U reviewu tražimo testove koji padaju kad se UX pokvari.

Unit i component testovi#

Provjeri

  • Testovi koriste user-centric upite i interakcije.
  • Izbjegavajte testiranje internog stanja ili privatnih funkcija.
  • Pokrijte error i empty stanja za kritične tokove.

Primjer: React Testing Library stil

TSX
it("submits the form", async () => {
  render(<UserForm />);
  await user.type(screen.getByLabelText("Email"), "a@b.com");
  await user.click(screen.getByRole("button", { name: "Save" }));
  expect(await screen.findByText("Saved")).toBeInTheDocument();
});

Što testirati, po riziku#

PodručjeMinimalno očekivanjeZašto je važno
FormeValidacija, submit uspjeh, submit greškaSprječava tihi gubitak podataka
NavigacijaPromjene ruta i ponašanje BackIzbjegava slomljene flowove
DozvoleUI stanja za neautorizirane korisnikeSprječava izloženost podataka
PristupačnostProvjere role i name, osnove fokusaZaustavlja regresije rano
Viewovi osjetljivi na performanseSmoke test za teške listeSprječava usporavanja

E2E testovi za korisnička putovanja#

Provjeri

  • Dodajte E2E testove za revenue-kritične tokove i kompleksne interakcije.
  • Držite E2E fokusiranima. Previše krhkih testova usporava isporuku.

# Predložak kontrolne liste za PR (GitHub i Linear) za ponovnu upotrebu#

Kopirajte ovo u predložak opisa PR-a ili Linear issue. Držite obavezne stavke kratkima i natjerajte autore da razmišljaju o utjecaju, ne samo o implementaciji.

Markdown
## Summary
- What changed:
- Why:
- User impact:
- Screenshots or video:
 
## React Code Review Checklist
### Components
- [ ] Component has a single clear responsibility
- [ ] Props are minimal, well-typed, and intent-based
- [ ] Loading, empty, and error states handled
 
### Hooks and effects
- [ ] `useEffect` dependencies correct and no stale closures
- [ ] Cleanup added for subscriptions, timers, and listeners
- [ ] Derived values are not duplicated in state
 
### State and data flow
- [ ] State ownership is clear and colocated where used
- [ ] No duplicated sources of truth
- [ ] Reducer or state machine considered for complex transitions
 
### Rendering and performance
- [ ] Lists use stable keys and avoid index keys
- [ ] No expensive computations inside render without memoization
- [ ] Memoization used only when it prevents real rerenders
 
### Accessibility
- [ ] Semantic elements used for interactive UI
- [ ] All inputs have labels and errors are announced
- [ ] Keyboard navigation works for the full flow
- [ ] Focus management handled for modals and overlays
 
### Testing
- [ ] Unit or component tests cover key behaviors and edge cases
- [ ] Tests query by role/label/text instead of implementation details
- [ ] E2E test added or updated for high-risk user journeys
 
## Risk notes
- Potential regressions:
- Rollback plan:

# Što automatizirati lint pravilima i CI provjerama#

Ako reviewer više puta komentira isti problem, to treba postati pravilo. Cilj je manje ljudskog vremena potrošenog na greške koje se mogu spriječiti.

Preporučeni baseline za automatizaciju#

CiljAlatiŠto sprječava
Konzistentno formatiranjePrettier u CI-ju i pre-commitRasprave o stilu i “bučne” diffove
Ispravnost hookovaeslint-plugin-react-hooksNedostajuće ovisnosti i stale closureove
Osnove a11yeslint-plugin-jsx-a11yNedostajuće labele, pogrešne role, keyboard trapove
Higijena importovaeslint-plugin-import, pravila protiv ciklusaCirkularne ovisnosti i neuredne granice modula
Type safetyTypeScript strict i noImplicitAnyRuntime greške i nejasne ugovore
Test gateUnit testovi u CI-ju, opcionalni coverage pragIsporuka bez sigurnosne mreže

Skica ESLint konfiguracije#

Neka bude kratko i pragmatično. Točna konfiguracija ovisi o vašem stacku, ali ovo su česti essentials.

JavaScript
module.exports = {
  extends: [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "plugin:jsx-a11y/recommended",
  ],
  rules: {
    "react/jsx-key": "error",
    "react/self-closing-comp": "warn",
    "jsx-a11y/label-has-associated-control": "error",
    "jsx-a11y/no-noninteractive-element-interactions": "error",
    "jsx-a11y/click-events-have-key-events": "error",
  },
};

CI provjere koje smatramo neupitnima#

  1. 1
    npm run lint
  2. 2
    npm run typecheck
  3. 3
    npm test
  4. 4
    Build korak za aplikaciju, posebno za Next.js rute

Dodajte sigurnosno skeniranje i provjere dependencyja kako codebase raste, usklađeno s našom kontrolnom listom za sigurnost web aplikacija.

🎯 Ključna poruka: Sve što se može pouzdano detektirati statičkom analizom treba automatizirati, kako bi revieweri mogli trošiti vrijeme na arhitekturu, UX, rizike performansi i tokove pristupačnosti.

# Ključne poruke#

  • Automatizirajte ponovljive stavke reviewa pomoću ESLint-a, TypeScript-a i CI-ja, a zatim usmjerite ljudski review na UX, a11y i rizike performansi.
  • Pregledajte komponente s fokusom na jasne odgovornosti, minimalne propse i namjerna loading, empty i error stanja.
  • Provjerite hookove i effecte: ispravne ovisnosti, cleanup i izbjegavanje dupliciranja izvedenog stanja.
  • Uhvatite regresije performansi rano provjerom stabilnih propsa, key-eva lista, skupog rada u renderu i virtualizacije za velike liste.
  • Tretirajte pristupačnost kao područje prve klase u reviewu: semantički HTML, pristupačna imena, tokovi tipkovnicom i ispravno upravljanje fokusom.
  • Tražite testove koji odražavaju ponašanje korisnika i pokrivaju kritične edge caseove, ne internu implementaciju.

# Zaključak#

Pouzdana kontrolna lista za React code review sprječava da se isti problemi stalno isporučuju: sporo renderiranje zbog nestabilnih propsa, nepristupačni UI patterni i krhka logika stanja. Ako standardizirate kontrolnu listu, automatizirate osnove i naučite reviewere da se fokusiraju na rizik, PR-ovi postaju brži, a aplikacija stabilnija.

Ako želite pomoć u uvođenju ove kontrolne liste kroz React ili Next.js codebase, ili trebate review trenutnih uskih grla u performansama i pristupačnosti, javite se Samiodi putem naših usluga web developmenta i prilagodit ćemo review proces vašem timu i ritmu izdanja.

FAQ

Share
A
Adrijan OmićevićOsnivač i senior developer

Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.