Állapotgépek összehasonlítása:
Az állapotkezelés a Reactban nehézkessé válhat, ahogy az alkalmazás logikája egyre összetettebbé válik. Az olyan harmadik féltől származó könyvtárak, mint a Redux, a Flux és a MobX segítenek, de még ezeknek az eszközöknek is megvan a maguk terhe.
Az állapotgép, más néven véges állapotgép vagy véges állapotú automata a számítás matematikai modellje. Ez egy absztrakt gép, amelynek egy adott időpontban véges számú állapota van.
Ezzel az útmutatóval áttekintjük két állapotgép – az XState és a Robot – hasonlóságait, különbségeit, előnyeit és hátrányait, és végigvesszük, hogyan használhatjuk őket a React alkalmazások állapotkezelésének egyszerűsítésére.
Miért használjunk állapotgépet?
Az állapot a legtöbb frontend alkalmazás fontos része, különösen a Reactban. Gondoljunk az állapotra úgy, mint az alkalmazás azon részének reprezentációjára, amelyik változik.
Gondoljunk egy olyan komponensre, amelyik adatokat hív le egy API-ból.
const Todo = () => { const = useState(); const handleClick = () => { fetch('https://jsonplaceholder.typicode.com/todos/1') .then(response => response.json()) .then(todo => setData(data.push(todo)) .catch(error => console.error(error) ) } return( <div> <button onClick={handleClick}> Fetch Data </button> {data && data.map(todo => (<p key={todo.id}> {todo.title} <span> {todo.completed} </span></p>) )} </div> ); }
Ebben a példában az adatok a mi állapotunk, mivel ez az alkalmazás azon része, amelyik változik, amikor egy esemény bekövetkezik – ebben az esetben egy gomb kattintása. A probléma ezzel a beállítással az, hogy bonyolulttá válhat.
Mi történik, amíg a felhasználó a rekord lekérdezésére vár, vagy ha a lekérdezés közben hiba lép fel? Több állapotot kell hozzáadnunk, hogy ezeket a problémákat kezelni tudjuk.
const Todo = () => { const = useState(false); const = useState(); const = useState(false); const handleClick = () => { setLoading(true); fetch('https://jsonplaceholder.typicode.com/todos/1') .then(response => response.json()) .then(todo => { setLoading(false); setData(data.push(todo)); }) .catch(error => { setLoading(false); setIsError(true); }) } return( <div> {loading && <p> Loading Data... </p>} <button onClick={handleClick}> Fetch Data </button> {data && data.map(todo => (<p key={todo.id}> {todo.title} <span> {todo.completed} </span></p>) )} {error && <p> An error occured. Try again.</p>} </div> ); }
Ha az alkalmazásunk összetett, a dolgok gyorsan kicsúszhatnak a kezünkből, ahogy új funkciókat adunk hozzá, ami megnehezíti a kód megértését, tesztelését és továbbfejlesztését.
Az állapotgépek másképp közelítik meg ezt az egyedi problémát. Az állapotgépekkel meghatározhatjuk az összes állapotot, amelyben az alkalmazásunk lehet, az állapotok közötti átmeneteket és a fellépő mellékhatásokat. Ez segít elkerülni azt a helyzetet, amikor az alkalmazás egy lehetetlen állapotban van.
Az alkalmazásunk a következő állapotokban lehet:
-
ready
– a kezdeti állapot, amikor az alkalmazás elindul -
loading
– amikor egy esemény bekövetkezik i.Pl. egy gomb megnyomása -
success
– amikor a betöltés megoldódik -
error
– amikor a betöltés elutasításra kerül
Az alkalmazás egyik állapotból a másikba akkor lép át, amikor egy művelet kiváltásra kerül – ill, amikor a felhasználó rákattint egy gombra. Jobban irányíthatod az alkalmazásodat, ha előre látod az összes lehetséges állapotot, amiben lehet.
Mit csinál az XState és a Robot?
A hivatalos dokumentációja szerint az XState egy könyvtár véges állapotgépek és állapotábrák létrehozására, értelmezésére és végrehajtására, valamint ezen gépek meghívásainak aktorként való kezelésére. David Khourshid hozta létre a felhasználói felületek állapotproblémáinak megoldására.
A Robot egy könnyű, funkcionális és megváltoztathatatlan könyvtár, amelyet Mathew Philips hozott létre véges állapotgépek létrehozására. Az XState, a Statecharts és a P programozási nyelv inspirálta.
Előfeltételek
A bemutató követéséhez szükséged lesz:
- Javascript ismeret
- React ismeret
- yarn vagy npm v5.2 vagy nagyobb
- Node 10-es vagy nagyobb verzió
Kezdés
Az XState és a Robot közötti hasonlóságok és különbségek bemutatásához létrehozunk egy alkalmazást, amely adatokat hív le egy API-ból.
Nyissunk meg egy terminált, és inicializáljunk egy React alkalmazást.
npx create-react-app state-machine
Ezzel létrehozunk egy State Machine nevű React alkalmazást.
Ezután hozzunk létre egy szolgáltatást az adatok API-ból való lehívásához.
cd src && touch fetchTodo.js
A fenti parancs létrehoz egy fetchTodo.js
nevű fájlt a src
könyvtárban.
Nyissuk meg a fájlt, és adjuk meg a következőket.
export const fetchTodo = () => { return fetch('https://jsonplaceholder.typicode.com/todos/1') .then((response) => response.json()) .then((todo) => todo);};
Lényegében bármikor, amikor a fetchTodo
függvényt meghívjuk, az API-ból lekért adatokat adja vissza.
Telepítés
Az XState telepíthető az npm vagy a yarn használatával, vagy a szkript beágyazásával egy CDN-en keresztül.
A könyvtár npm segítségével történő telepítéséhez nyissunk terminált és futtassuk:
npm install xstate @xstate/react
Ez telepíti a xstate
core könyvtárat és egy @xstate/react
nevű csomagot a React számára, amely lehetővé teszi az egyéni XState horgok használatát a React alkalmazásokban.
A Robotot az npm vagy a yarn segítségével telepítheti, de CDN-t nem használhat.
A Robot telepítéséhez indítson el egy terminált, és futtassa a következő parancsot.
npm install robot3 react-robot
A Robot egy csomagot is kínál az egyéni horgok Reactben való használatához react-robot
Gép létrehozása
Az állapotgép használata előtt először definiálnunk kell azt.
A src
könyvtárban hozzunk létre egy xstateMachine.js
nevű fájlt. Másoljuk az alábbi kódot a létrehozott fájlba.
import { Machine, assign } from 'xstate';import { fetchTodo } from '../fetchTodo';export const xstateMachine = Machine({ id: 'clickButton', initial: 'ready', context: { todo: null, }, states: { ready: { on: { CLICK: 'loading', }, }, loading: { invoke: { id: 'fetch-todo', src: fetchTodo, onDone: { target: 'success', actions: assign({ todo: (context, event) => event.data, }), }, onError: 'error', }, }, success: { on: { CLICK: 'loading', }, }, error: { on: { CLICK: 'loading', }, }, },});
A gépeket a Machine()
gyári függvény segítségével definiáljuk. A fenti kódban definiált gép ID-kből, állapotokból, kontextusokból, akciókból és átmenetekből áll. Az ID-k az állapotcsomópontok azonosítására szolgálnak.
A gép állapotai a következők:
ready
loading
success
error
A kontextus egy kiterjesztett állapot, amelyet mennyiségi adatok, például számok, tetszőleges string, objektumok stb. megjelenítésére használunk. Az alkalmazás kezdeti állapota ready
ként van definiálva. Amikor egy gombra kattintunk, egy átmenet történik, amely az állapotot a ready
állapotból a loading
állapotba helyezi át.
A loading
állapotban van egy invoke
tulajdonság, amely egy ígéret feloldásáért vagy elutasításáért felelős. Amikor a fetchTodo
ígéret feloldásra kerül, a loading
állapot átmegy a success
állapotba, és a assign
művelet frissíti a kontextust az ígéretből kapott eredménnyel. Visszautasítás esetén a error
állapotba lép.
A Robot segítségével egy gép létrehozása hasonló, bár néhány lényeges különbséggel. Az egyik fő különbség az, hogy mivel a Robot egy funkcionális könyvtár, a legtöbb műveletet függvények segítségével hajtjuk végre, ellentétben az XState-tel, amely opciós objektumokat használ.
Készítsünk egy robotMachine.js
nevű fájlt a src
könyvtárunkban, és illesszük be a következőket.
import { createMachine, invoke, reduce, state, transition } from 'robot3';import { fetchTodo } from '../fetchTodo';const context = () => ({ todo: {},});export const robotMachine = createMachine( { ready: state(transition('CLICK', 'loading')), loading: invoke( fetchTodo, transition( 'done', 'success', reduce((ctx, evt) => ({ ...ctx, todo: evt.data })) ), transition( 'error', 'error', reduce((ctx, ev) => ({ ...ctx, error: ev.error })) ) ), success: state(transition('CLICK', 'loading')), error: state(transition('CLICK', 'loading')), }, context);
A Robotban a gépek létrehozása a createMachine
függvény segítségével történik, amely egy objektumot fogad el. Egy állapotot a state
függvénnyel definiálunk, és paraméterként elfogadhat egy transition
függvényt.
Az egyik állapotból a másikba való átlépés a transition
függvénnyel történik, amely paraméterként elfogadja az eseményt és a következő állapotot. Opcionálisan a transition
függvényhez harmadik paraméterként hozzáadható a reduce
függvény. A reduce függvények paraméterként egy reducer függvényt fogadnak el, amely a kontextus frissítésére szolgál.
A robotnak van egy invoke
függvénye is, amely hasonló az XState invoke
tulajdonságához. Amikor az alkalmazás loading
állapotban van, a invoke
függvényt hívja meg. A invoke
függvény egyfajta állapot, amely egy ígéretet hív meg, és egy függvényt vagy egy másik gépet ad vissza. Ha a invoke
függvény feloldja az ígéretet, akkor egy done
eseményt küld. Ha elutasítja, akkor egy error
eseményt küld.
A komponens megépítése
Most, hogy a gépünk készen áll, a következő lépés a gépet használó komponens megépítése.
Készítsünk egy fájlt a src
könyvtárunkban a komponenshez, és illesszük be a következőket.
import React from 'react';import { useMachine } from '@xstate/react';import { xstateMachine } from './stateMachine';function Todo() { const = useMachine(xstateMachine); const { todo } = current.context; return ( <div> <button onClick={() => send('CLICK')}>Fetch Todo XState</button> {current.matches('loading') && <p>loading...</p>} {current.matches('success') && ( <p key={todo.id}> {todo.title} <span> {todo.completed} </span> </p> )} {current.matches('error') && <p>An error occured</p>} </div> );}export default Todo;
A gép használatához importálnunk kell a useMachine
hook-ot a @xstate/react library
-ből, valamint a korábban létrehozott gépet.
A useMachine
hook egy React Hook, amely értelmezi a gépet. Felelős egy szolgáltatás elindításáért, hogy az egy komponens teljes életciklusa alatt fusson.
A useMachine
horog egy gépet fogad el paraméterként, és egy tömböt ad vissza. A tömb tartalmazza a current
állapotot és a send
-t, amely egy olyan függvény, amely eseményt küld a useMachine
hook által létrehozott szolgáltatásnak.
A current
állapot egy objektum, amely tartalmazza az állapotot, a kontextust és néhány segédfüggvényt. Az aktuális állapot ellenőrzéséhez használja a matches
tulajdonságot, amely egy boolean értéket ad vissza. Amikor a felhasználó rákattint a gombra, az eseményt küld a szolgáltatásnak. Ezután az ellenőrzi a gép aktuális állapotát, és az állapot alapján megjeleníti a megfelelő felhasználói felületet.
A Robot megközelítése a komponensek építéséhez hasonló. Egy Robot segítségével épített komponens így nézne ki:
import React from 'react';import { useMachine } from 'react-robot';import { robotMachine } from './robotMachine';function Todo() { const = useMachine(robotMachine); const { todo } = current.context; return ( <div> <button onClick={() => send('CLICK')}>Fetch Todo Robot</button> {current.name === 'loading' && <p>loading...</p>} {current.name === 'success' && ( <p key={todo.id}> {todo.title} <span> {todo.completed} </span> </p> )} {current.name === 'error' && <p>An error occured</p>} </div> );}export default RobotTodo;
A Robotnak van egy useMachine
kampója is, amelyet a react-robot library
importálásával érhetünk el. A különbség a megvalósításban abban van, ahogyan egy állapotot összehasonlítanak. Míg az XState a matches
tulajdonságot használja, ami egy függvény, amely elfogadja az összehasonlítani kívánt karakterláncot, addig a Robot a name
tulajdonságot használja az aktuális állapot ellenőrzésére az összehasonlítás előtt.
Következtetés
Az állapotgépek sokkal jobban szervezett módot kínálnak az állapot kezelésére a React alkalmazásokban, és más alternatívákhoz képest könnyen skálázhatóak. Az XState és a Robot két nagyon népszerű könyvtár, amelyek bár nagyon hasonlóak, nagyon különböző nézőpontból közelítik meg az állapotgépek kezelését.
Az ehhez a bemutatóhoz tartozó tároló elérhető a GitHubon.
További információkért nézd meg a következő forrásokat.
- Robot dokumentáció
- XState dokumentáció
- “Infinitely Better UIs with Finite Automata” (videó)
Teljes átláthatóság a produktív React alkalmazásokban
A React alkalmazások hibakeresése nehéz lehet, különösen, ha a felhasználók olyan problémákat tapasztalnak, amelyeket nehéz reprodukálni. Ha érdekel a Redux állapotának figyelése és nyomon követése, a JavaScript hibák automatikus felszínre hozása, valamint a lassú hálózati kérések és a komponensek betöltési idejének nyomon követése, próbáld ki a LogRocket-t.
A LogRocket olyan, mint egy DVR a webes alkalmazásokhoz, amely szó szerint mindent rögzít, ami a React alkalmazásodban történik. Ahelyett, hogy találgatnád, miért történnek problémák, összesíteni és jelenteni tudod, hogy milyen állapotban volt az alkalmazásod, amikor a probléma felmerült. A LogRocket figyeli az alkalmazás teljesítményét is, és olyan mérőszámokkal készít jelentést, mint az ügyfél CPU-terhelése, az ügyfél memóriahasználata és így tovább.
A LogRocket Redux middleware csomag a felhasználói munkamenetek átláthatóságának egy extra rétegét adja hozzá. A LogRocket naplózza a Redux tárolók összes műveletét és állapotát.
Modernizálja a hibakeresés módját React alkalmazásaiban – kezdje el a monitorozást ingyen.