State Management in de Front-end

-

Ik wil het graag over ‘states’ in de frontend hebben. Bij Roparun hadden we de ‘ngrx store’ library gebruikt om state bij te houden (eigenlijk gewoon volgens Redux principe, maar dan met wat extra’s). Zie hieronder.

Het framework dat we bij Roparun hebben gebruikt was Angular (9 volgens mij), ook handig om te weten. In het plaatje zie je dat de ‘UI’ actions triggert, onder UI vallen bij het Roparun-project de components. Naast de blokjes hierboven hadden we ‘Effects’ die luisteren naar Actions en API-calls triggeren, waarbij de response weer door een nieuwe Action heen ging (dus de manier om state bij te werken met data van de API). Ten slotte hadden we ‘Selectors’, een afgescheiden manier om dat voor elk component anders te mappen tussen ‘State’ en ‘UI’, wat als voordeel heeft dat je geen gekke side effects krijgt.

Zelf ben ik heel positief over dit principe. Ik vind het in het kort overzichtelijk, makkelijk te lezen, schaalbaar en makkelijk toe te passen. Als je eenmaal weet hoe het werkt, werkt het super chill. Wat wel minder is is dat je zo veel verschillende files, tenminste dat is wat je er zelf van kan maken, dus scheiding hebt, dat je vaak heen en weer moet schakelen. Bij het Medigrid-project hebben we geen store gebruikt, maar de state bijgehouden bij de services zonder Redux principe. Dat zorgt in principe dus voor dit:

Bij dit project heb ik trouwens voor het eerst DTO-mapping gezien en ik ben er wel echt fan van, maar dat terzijde. Voor voorbeelden zal ik even de ‘DataSetService’ pakken van het project. De standaard “actions” die we in de service gebruikten waren gewoon de CRUD-acties. We hadden alleen een scheiding gemaakt tussen ‘get’ and ‘fetch’, waarbij we fetch nooit mochten aanroepen vanaf de components.

Get all
‘getDataSets’ zorgde ervoor dat wanneer er geen call bezig is en als er nog geen datasets gefetched zijn, dat ‘fetchDataSets’ werd aangeroepen. Daarnaast returnde de method altijd een observable, waar de dataSets dus doorheen komen wanneer de state wijzigt.

 

State (ik ga hier elke variabele langs in het plaatje)
dataSets
De state was een object dataSets, dus geen array. Waarom? Om makkelijker de state bij te kunnen werken, mits één dataSet in de state bijgewerkt moet worden, zonder dat je door een hele array hoeft te lopen.


dataSetsSubject
Sommigen vragen zich misschien af, wat is een BehaviorSubject? Ik leg even in het kort uit hoe we hem hebben gebruikt, dan is het waarschijnlijk wel duidelijk. In het Get all onderwerp zie je in het plaatje dat in getDatasets, dataSetsSubject word gereturned als Observable (iets waar components naar kunnen luisteren, zodat ze altijd de laatste data hebben dat door die observable heen gaat). In de fetch zie je dat setDataSets wordt aangeroepen. Die zorgt ervoor dat er nieuwe data komt door de observable:


BehaviorSubject zorgt er dus in principe voor dat je nieuwe data door kunt passen. BehaviorSubjects hebben een initiele value die je zelf moet geven, keep that in mind.

dataSetsFetched en fetchingDataSets
Om te checken of we het al in cache hebben. Waarom niet checken of er iets in dataSets zit vraag je je af? Om ervoor te zorgen dat de fetch opnieuw uitgevoerd kan worden zonder de state leeg te plempen mocht de call fout gaan.

dataSetSubjects
Mocht je naar een enkele dataSet willen luisteren, kan dat 😊
Hierbij de get fetch set combo:

Create

De Create voegt gewoon een dataSet toe aan de dataSets state door setDataSet aan te roepen:

setDataSet zorgt ervoor dat hij aan de dataSets state word toegevoegd, dat er een subject ervoor word gemaakt indien die niet bestaat en de dataSet word die subject geduwd. Ten slotte word setDataSets nog aangeroepen in setDataSet met de dataSets state als arg, zodat die ook gerefreshed.

 

Update

Hier moeten we de dataSet in de state updaten, dat heb je eigenlijk het id alleen nodig en eventuele data om het te updaten. Hieronder zie je hoe simpel je eigenlijk de dataSet word geupdate en daarna setDataSet word aangeroepen met de bewerkte dataSet. In dit geval hadden we nieuwere data nodig voor een andere state binnen de service, dus hadden we dat binnen die state voor een bepaald id de data gecleared en voor dat id dat de ‘data is fetched” op false gezet, zodat de fetch getriggerd kon worden.

 

Delete

Hebben we eigenlijk er niet ingebouwd, maar eigenlijk zou een property in het object gedelete moeten worden op basis van het dataSet id.

 

Meer is het eigenlijk niet, zelf vind ik dit principe voor kleine projecten fijner. Wat ik alleen mis hier zijn de selectors. Mocht je de data op een andere manier bij je component willen en is het zo dat je het ook bij een paar andere componenten op die manier wil hebben, dan zou je nu of een extra state moeten maken of een util / mapper die het voor je mapt.

Bij Roparun gebruikten we Redux voor state bijhouden. Bij Medigrid een compactere manier. Ik ben zelf van mening dat Redux fijner is voor grotere projecten en de compactere manier voor kleine.