Lekce 1.9

Tři pravidla pro vedlejší efekty v Reactu

Pokud jste s Reactem pracovali už delší dobu, pravděpodobně jste někdy zírali na dependency pole useEffectu a přemýšleli, proč se komponenta re-renderuje v nekonečné smyčce. Nebo jste narazili na chybu „Hydration failed“, protože něco v komponentě neodpovídalo výstupu ze serveru.

Co se naučíte
  • Co je to vedlejší efekt a jak souvisí se vztahem v = f(s)
  • Proč tělo komponenty musí zůstat čistým výpočtem (Rule #0)
  • Kdy patří vedlejší efekty do event handlerů (Rule #1)
  • Kdy patří vedlejší efekty do useEffect pro synchronizaci (Rule #2)
  • Jak použít jednoduchou rozhodovací matici pro umístění logiky

Kořen většiny těchto bolestí hlavy je téměř vždy stejný: špatná práce s vedlejšími efekty. Dobrá zpráva je, že si nemusíte pamatovat všechny nuance lifecycle metod – stačí jednoduchý mentální model se třemi pravidly.

Tento článek je praktický, bez zbytečného žargonu: pomůže vám dát každý kus logiky na správné místo tak, aby vaše komponenty byly rychlé, předvídatelné a bezpečné z pohledu hydration.

Základ: v = f(s)

Abychom pochopili, kam vedlejší efekty patří, musíme si ujasnit, co je to vlastně React komponenta. V jádru React následuje jednoduchou matematickou myšlenku:

text
view = f(state)

Vaše komponenta je funkce. Jako vstup bere state (a props) a vrací popis UI jako výstup. Jinými slovy, vaše View je funkcí vašeho stavu: v = f(s).

Vedlejší efekt je všechno, co se děje mimo tento čistý výpočet.

  • Výpočet count * 2? To není vedlejší efekt, to je jen matematika.
  • Změna document.title? Vedlejší efekt – měníte prostředí prohlížeče.
  • Načítání dat? Vedlejší efekt – komunikujete se serverem.
  • console.log? Vedlejší efekt – zapisujete do konzole.

Cílem Reactu je udržet proces renderování — výpočet View — čistý a rychlý. Tři následující pravidla existují proto, aby tuto čistotu chránila.

Pravidlo #0: Zlaté pravidlo renderování

Pravidlo: Funkce, která počítá View, musí být čistý výpočet.

Když React volá vaši komponentu, očekává popis UI – nic víc. Tělo komponenty je posvátné místo: patří sem jen čisté výpočty.

Častou chybou je dát vedlejší efekty přímo do těla funkce:

jsx
function UserProfile({ name }) {
  // ❌ VIOLATION: Modifying an external system during render
  document.title = `Profile: ${name}`

  return <h1>Hello, {name}</h1>
}

Proč je to problém

  • React volá komponentu mnohokrát — když se renderuje rodič, změní state nebo jen kvůli interním kontrolám. Spouštění vedlejších efektů v tomto místě render zpomaluje.
  • Rozbíjí server-side rendering (SSR): na serveru neexistuje document, takže tento kód spadne.
  • Chování se stává nepředvídatelným, protože ztrácíte kontrolu nad tím, kdy se efekt spouští.

Oprava: nikdy nedávejte vedlejší efekty do top‑levelu komponenty. Udržte render čistý; v Pravidlech #1 a #2 uvidíme, kam tuto logiku přesunout.

Pravidlo #1: Uživatelské akce patří do event handlerů

Pravidlo: Pokud je vedlejší efekt spuštěn konkrétní událostí (click, submit, psaní…), patří do event handleru.

Zní to samozřejmě, ale jde o jedno z nejčastěji porušovaných pravidel. Typickým anti‑vzorem je používat kombinaci state + useEffect jako vlastní event systém.

Anti‑vzor

Představte si, že chcete poslat analytiku, když uživatel klikne na notifikační badge:

jsx
function NotificationBadge() {
  const [count, setCount] = useState(0)
  const [clicked, setClicked] = useState(false)

  // ❌ VIOLATION: Using state & effect for a simple action
  useEffect(() => {
    if (clicked) {
      sendAnalytics("badge_clicked")
      setClicked(false) // Resetting state? It's getting messy.
    }
  }, [clicked])

  return (
    <button
      onClick={() => {
        setCount(0)
        setClicked(true)
      }}
    >
      Notifications
    </button>
  )
}

Skutečnou příčinou vedlejšího efektu je kliknutí uživatele. Efekt je ale navázaný na kus state, což přináší zbytečné rendery i složitost.

Oprava

jsx
function NotificationBadge() {
  const [count, setCount] = useState(0)

  function handleClick() {
    // 1. Update State (UI Logic)
    setCount(0)

    // 2. Run Side Effect (Business Logic) ✅
    sendAnalytics("badge_clicked")
  }

  return <button onClick={handleClick}>Notifications</button>
}
  • React ani neví, že k vedlejšímu efektu došlo – vidí jen aktualizaci state.
  • Render zůstává čistý; handler běží až po uživatelské akci.
  • Pro tento scénář nepotřebujete useEffect vůbec.

Pravidlo #2: Synchronizujte pomocí useEffect

Pravidlo: Pokud vedlejší efekt udržuje komponentu v synchronizaci s vnějším systémem, patří do useEffect.

To je jediný skutečný důvod, proč používat useEffect: synchronizace.

Co všechno je „vnější systém“?

  • Databázové nebo API připojení
  • WebSocket nebo jiný subscription
  • DOM prohlížeče (např. document.title)
  • Časovače jako setTimeout nebo setInterval

Pokud chcete, aby vaše komponenta zůstala sladěná s jedním z těchto systémů po dobu, kdy je na obrazovce, je useEffect správný nástroj.

Příklad: document.title

Vraťme se k příkladu s document.title, tentokrát implementovanému správně se zaměřením na synchronizaci:

jsx
function NotificationBadge({ count }) {
  // ✅ CORRECT: We are synchronizing the DOM with our State
  useEffect(() => {
    document.title = `(${count}) New Messages`

    return () => {
      document.title = "React App"
    }
  }, [count])

  return <div className="badge">{count}</div>
}

Nepřemýšlejte o dependency array jako o „kdy se tento kód spustí“. Myslete na něj jako na „jaká data efekt používá k synchronizaci“.

Když se count změní, předchozí synchronizace už neplatí. React ji zruší a efekt znovu spustí, aby vnější systém uvedl do souladu s novým stavem.

Rozhodovací matice: Kam tato logika patří?

Příště, až budete psát komponentu a nebudete si jistí, kam logiku umístit, projděte si tento jednoduchý rozhodovací strom:

  1. Je to jen výpočet View?

    Pokud ano, patří to přímo do těla komponenty. To je logika renderování. (Pravidlo #0)

  2. Spustila to konkrétní uživatelská nebo systémová událost (kliknutí, submit, stisk klávesy…)?

    Pokud ano, patří to do event handleru. To je logika akce. (Pravidlo #1)

  3. Potřebuji udržovat komponentu v čase v synchronizaci s vnějším systémem?

    Pokud ano, patří to do useEffect. To je synchronizační logika. (Pravidlo #2)

Pokud vaše logika zjevně nezapadá do žádné z těchto kategorií, zkuste úlohu zjednodušit. Většina reálných bugů kolem vedlejších efektů vzniká tím, že se tyto tři oblasti smíchají dohromady.

Shrnutí

  • React komponenty jsou čisté funkce z props/state do View: v = f(s).
  • Tělo komponenty musí zůstat čisté – žádné vedlejší efekty. (Pravidlo #0)
  • Vedlejší efekty spuštěné konkrétní událostí patří do event handlerů. (Pravidlo #1)
  • useEffect používejte pouze pro synchronizaci s vnějšími systémy v čase. (Pravidlo #2)

Jakmile si tato tři pravidla osvojíte, 99 % práce s vedlejšími efekty se stane přímočarou – a nekonečné smyčky či hydration chyby budou vzácné a snadno opravitelné.