TODO App Fight

Round 1

React, RxJS, partial.lenses

25th Nov 2019


TODO-appistappelu

Kierros 1

React, RxJS, partial.lenses

Eli kuinka hallita tilaa RxJS:llä ja käyttää Reactia vain tilan renderöintiin*

(* React-komponentti voi silti hyödyntää paikallista tilaa)

25.11.2019


Millainen on TODO-appis?

(demo)


Immutable-tilapuu, motivaatio

Esitämme sovelluksen tilan puumaisena rakenteena (tilapuu), jonka juurena on objekti.

Haluamme tilapuun olevan immutable, jotta sen päivitys tapahtuu aina eksplisiittisesti:

fn(oldState) => newState

Tämä helpottaa ohjelman logiikan seuraamista ja vähentää bugeja.

Välitämme React-komponenteille vain tarvittavat osat tilapuusta.

Mikään React-komponentti ei voi mutatoida saamaansa tilapuun osaa.


Immutable-tilapuu, toteutus

Mahdollistamme tämän seuraavasti:


partial.lenses

partial.lenses on lens-abstraktiota käyttävä kirjasto, jolla voi hakea ja päivittää osia immutable-tietorakenteista, tuottaen niistä uusia versioita.

Kirjasto erottelee datan osoituksen (lenses) datan muuttamisesta.

Arvon päivitys objektiin:

const m = {a: {b: 1}, c: 2}
L.set(["a", "b"], 3, m) // => {a: {b: 3}, c: 2}

Objektien yhdistäminen:

const m = {a: {b: 1}}
L.assign("a", {c: 2}, m) // => {a: {b: 1, c: 2}}

Valittu tilapuu

{
  // the last event triggering a change to the state; for self-inspection
  lastEvent: {
    type: string,
    // plus event data, such as task id; depends on event type
  },
  // data model, to be consumed by React components
  model: {
    taskFilterText: string,
    tasks: [
      {id: uuid, isDone: boolean, name: string}
      // ...
    ]
  }
}

React, komponenttihierarkia

App
|
+- SearchTaskField
|
+- TaskList
|  |
|  +- Task
|  +- Task
|  `- Task
|
`- AddTaskField

React, tilanhallinta

Määritellään alkutila (startState, useReducer).

Tila välitetään propseina React-komponenteille.

Komponenteilla voi olla oma sisäinen tila, esimerkiksi tallentamaan input-elementin teksti (useState SearchTaskField-komponentissa).

Tila ja komponentit päivittyvät Reactin hallinnoimista DOM-tapahtumista (click, keydown; React controlled component).

(demo)

Kuinka päivittää komponentti DOM:n ulkopuolelta?

App-komponentti renderöityy, vaikka normalisoitu hakuteksti ei muutu.


RxJS

Single Multiple
Pull function Iterator
Push Promise Observable

Observable: producer of many values (events), pushes them to Observers (subscribers).

Each Observer gets an independent execution of the Observable subscribed to (unicast).

Subject: special Observable that allows shared execution to many Observers (multicast). Like pub-sub.


RxJS ja React

Tilaa hallitaan RxJS:llä, Reactilla renderöidään tila.

(demo)


Miksi tämä kaikki?

(Palpatinen ähkäisy lopussa johtuu mahdollisen konffauksen määrästä.)


Ratkaisun hyödyt

Sovellusten rakentaminen kirjastoilla, jotka sopivat hyvin yhteen, antaa paljon joustavuutta suunnitteluun.

Vertaa sovelluskehyksen käyttöön, jossa päätökset on jo tehty puolestasi. Sopiiko sovelluskehys ongelmaasi, tunnetko edes ongelmaa hyvin projektin alussa?

Mahdollistaa kirjastojen vaihtamisen, jos kirjastojen paradigma on suunnilleen samanlainen. Esimerkiksi:

Tilanhallinta on aina eksplisiittistä. Sinä olet ohjaksissa!


Ratkaisun hyödyt

React-komponenttien koodi on intuitiivista.

"Great programmers write baby code"

– Eric Meijer, Principles of Reactive Programming -verkkokurssi


Kokemuksia RxJS:stä

Deklaratiivinen API on aluksi hankala käyttää:

Soveltuu erityisen hyvin async-tapahtumien hallintaan.

Mahdolliset bugit johtuvat väärästä tavasta mallintaa tapahtumia (events).

RxJS on ollut olemassa useita vuosia. Vakaa ja suorituskykyinen.


Ratkaisun haitat

Vaatii jonkin verran boilerplate-koodia, mikä voi aluksi turhauttaa.

Takaisinmaksu tulee toteuttaessa sovelluksia, joissa vaaditaan monimutkaista tilanhallintaa.

Tilanhallinnan yhdistävä Observable (stateO) sijoittuu sovelluksen ylimmälle tasolle.

Demon controller-abstraktio on hieman kömpelö, mutta mielestäni välttämätön, jottei actionS (Subject) välity React-komponenteille (liikaa valtaa).


Lue ja tutki lisää

(Esitys toteutettu Hacker's Tiny Slide Deckillä.)