Valtio ir Jotai: Reakcijos būsenos atomai

Kas tie state atoms ir kodėl visi apie juos kalba?

Jei dirbi su React, tikrai esi susidūręs su state valdymo problema. Komponentai auga, props drilling tampa košmaru, o Context API pradeda jaustis kaip lėtas ir nepatogus sprendimas. Čia ir ateina atomic state management bibliotekos – Valtio ir Jotai. Abi sukurtos Poimandres kolektyvo, abi žada paprastumą, bet veikia visiškai skirtingai.

Atomic state management idėja paprasta: vietoj vieno didelio store, kuriame gyvena visas aplikacijos state’as, tu kuri mažus, nepriklausomus state gabalėlius – atomus. Komponentai užsiprenumeruoja tik tai, kas jiems reikalinga, ir re-renderinasi tik tada, kai pasikeičia jų naudojami duomenys. Skamba puikiai, tiesa? Bet kaip tai atrodo praktikoje su Valtio ir Jotai?

Valtio: proxy magia ir mutability

Valtio yra įdomus žvėris. Jis naudoja JavaScript Proxy API, kad sektų state pasikeitimus. Tai reiškia, kad tu tiesiog… keiti objektus. Taip, tiesiog keiti juos, kaip darytum su paprasta JavaScript struktūra. Jokių setter funkcijų, jokių sudėtingų API – tiesiog priskirk naują reikšmę.

import { proxy, useSnapshot } from 'valtio'

const state = proxy({
count: 0,
user: { name: 'Jonas', age: 25 }
})

function Counter() {
const snap = useSnapshot(state)
return (

{snap.count}

)
}

Matai tą state.count++? Tai ne klaida. Valtio leidžia tiesiogiai mutuoti state, o proxy mechanizmas automatiškai seka pasikeitimus ir atnaujina komponentus. Tai jaučiasi labai natūraliai, ypač jei ateini iš Vue ar MobX pasaulio.

Bet štai kur įdomiausia dalis – useSnapshot grąžina immutable snapshot. Tu skaitai iš snapshot, bet rašai į originalų proxy objektą. Tai gali atrodyti keista iš pradžių, bet praktikoje veikia puikiai ir apsaugo nuo atsitiktinių mutacijų render metu.

Jotai: atomai kaip primitive values

Jotai eina visiškai kitu keliu. Čia atomai yra primitive reikšmės – mažiausi galimi state gabalėliai. Kiekvienas atomas yra nepriklausomas, ir tu juos komponuoji į sudėtingesnes struktūras.

import { atom, useAtom } from 'jotai'

const countAtom = atom(0)
const userAtom = atom({ name: 'Jonas', age: 25 })

function Counter() {
const [count, setCount] = useAtom(countAtom)
return (

{count}

)
}

API atrodo pažįstamas, tiesa? Tai beveik kaip useState, tik state’as gyvena už komponento ribų. Bet Jotai tikroji galia atsiskleidžia su derived atoms ir async atoms.

const doubleCountAtom = atom((get) => get(countAtom) * 2)

const userDataAtom = atom(async (get) => {
const userId = get(userIdAtom)
const response = await fetch(`/api/users/${userId}`)
return response.json()
})

Derived atomai automatiškai perskaičiuojami, kai pasikeičia jų priklausomybės. Async atomai leidžia valdyti asinchroninę logiką tiesiogiai state layer’yje. Tai galinga, bet reikalauja kitokio mąstymo būdo nei įprastai.

Performance: kas greitesnis?

Čia reikalai tampa įdomūs. Abi bibliotekos optimizuotos re-rendering prevencijai, bet skirtingais būdais.

Valtio proxy mechanizmas automatiškai seka, kurie property’ai buvo naudoti komponente. Jei komponentas skaito tik state.count, jis nere-renderinsis, kai pasikeis state.user. Tai veikia automatiškai, be jokio papildomo kodo. Problema – proxy overhead. Kiekvienas property access eina per proxy trap, o tai turi kainą.

Jotai naudoja subscription modelį. Komponentas užsiprenumeruoja konkretų atomą, ir re-renderinasi tik kai tas atomas pasikeičia. Čia nėra proxy overhead, bet tu turi būti atidus su atom granularity. Per stambūs atomai = nereikalingi re-renders. Per smulkūs = per daug boilerplate.

Praktiškai? Daugumoje aplikacijų skirtumas nematomas. Bet jei dirbi su dideliais objektų medžiais ir dažnais update’ais, Valtio proxy overhead gali tapti problema. Jotai tokiose situacijose paprastai laimi, ypač jei teisingai sustruktūruoji atomus.

Developer experience: kur maloniau gyventi?

Čia subjektyvu, bet turiu savo nuomonę po metų darbo su abiem bibliotekomis.

Valtio DX yra fantastiškas pradedantiesiems. Tu tiesiog keiti objektus, ir viskas veikia. Nereikia galvoti apie atomų struktūrą, dependency grafus ar subscription modelius. TypeScript support puikus – tipo sistema supranta proxy struktūrą ir teikia normalų autocomplete.

Bet kai aplikacija auga, Valtio gali tapti chaotiška. Kadangi state’as yra vienas didelis objektas (ar keli dideli objektai), sunku suprasti, kas nuo ko priklauso. Mutations gali ateiti iš bet kur, ir debugging tampa sudėtingesnis.

Jotai reikalauja daugiau pradinio mąstymo. Tu turi suplanuoti atomų struktūrą, suprasti, kaip jie tarpusavyje sąveikauja. Bet kai tai padarai, aplikacijos architektūra tampa aiškesnė. Kiekvienas atomas turi aiškią atsakomybę, dependency grafas yra eksplicitiškas.

Integracijos su ekosistema

Abi bibliotekos gerai integruojasi su React ekosistema, bet yra niuansų.

Valtio puikiai veikia su React DevTools, bet debugging gali būti sudėtingas, nes matai tik proxy objektus. Yra devtools utility, bet jis nėra toks galingas kaip Redux DevTools. Persistence (localStorage, sessionStorage) reikalauja papildomų bibliotekų ar custom sprendimų.

Jotai turi oficialius utils package’us beveik viskam: jotai/utils teikia atomWithStorage, atomWithReducer, atomFamily ir daugybę kitų helper’ių. Yra ir jotai/devtools integracija, kuri veikia su Redux DevTools. React Suspense support yra first-class – async atomai natūraliai veikia su Suspense boundaries.

Jei naudoji Next.js ar kitą SSR framework, Jotai turi geresnį story. Provider komponentas leidžia turėti skirtingą state server ir client side, o useHydrateAtoms padeda su hydration. Valtio SSR support yra, bet reikalauja daugiau manual darbo.

Kada rinktis vieną ar kitą?

Po viso šito teorijos, praktinis klausimas: kurį pasirinkti savo projektui?

Rinkis Valtio, jei:

– Dirbi su sudėtingais nested objektais ir nori paprastos mutability
– Tau patinka MobX ar Vue reactivity modelis
– Reikia greitai pradėti be daug architektūrinių sprendimų
– Aplikacija nedidelė-vidutinė ir performance nėra kritinis faktorius
– Komandoje yra žmonių, kurie bijo „per daug abstraktaus” kodo

Rinkis Jotai, jei:

– Nori aiškios, kompozuojamos state architektūros
– Dirbi su daug async logikos (API calls, real-time data)
– Reikia gero SSR/SSG support
– Performance yra svarbu ir nori fine-grained reactivity
– Patinka functional programming principai ir eksplicitiškas dependency management

Asmeniškai, naujuose projektuose linkstu į Jotai. Taip, reikia daugiau pradinio planavimo, bet ilgalaikėje perspektyvoje aplikacija lieka maintainable. Valtio puikus prototyping’ui ar mažesniems projektams, kur paprastumas svarbiau už struktūrą.

Migracija ir hibridiniai sprendimai

Įdomus faktas: gali naudoti abi bibliotekas viename projekte. Tai nėra standartinis use case, bet kartais prasminga.

Pavyzdžiui, gali turėti Valtio global UI state (modals, notifications, theme) – dalykams, kurie dažnai keičiasi ir nereikalauja sudėtingos logikos. O Jotai naudoti domain state’ui – user data, business logic, async operations.

Migracija iš vienos į kitą nėra sudėtinga, nes abi bibliotekos mažos ir neįkyriai integruojasi. Gali migruoti po vieną feature, palikdamas seną state management likusiai aplikacijos daliai.

Jei migruoji iš Redux, Jotai bus natūralesnis pasirinkimas – atomWithReducer leidžia naudoti reducer pattern, kurį jau žinai. Valtio reikalaus daugiau mentalinio shift’o nuo immutability prie mutability.

Ką pasirinkti ir kaip nepasimesti

Atomic state management nėra silver bullet, bet abi šios bibliotekos siūlo gaivų požiūrį į React state valdymą. Valtio žavi paprastumu ir natūraliu API, Jotai – galia ir kompozuojamumu.

Mano patarimas: išbandyk abi mažame projekte ar feature’e. Padaryk paprastą todo app ar dashboard su kiekviena. Pajusi, kuri labiau atitinka tavo mąstymo būdą ir projekto poreikius. Dokumentacija abiejų puiki, community aktyvi, o bundle size minimali (Valtio ~3kb, Jotai ~2.5kb).

Ir nepamirsk – state management biblioteka neišspręs architektūrinių problemų. Jei tavo aplikacija chaotiška su Redux, ji bus chaotiška ir su Valtio ar Jotai. Bet jei turi aiškią viziją ir nori įrankio, kuris netrukdys, o padės – abi šios bibliotekos yra puikus pasirinkimas 2024 metais.

Daugiau

„Apache Pulsar“ ir „Kafka“: pranešimų srautas