Tervetuloa takaisin toiseen jännittävään erään ClojureScriptin löytäminen ! Tässä viestissä aion käsitellä seuraavaa suurta askelta vakavampaan ClojureScriptiin: valtionhallinta - tässä tapauksessa Reactin avulla.
Etupään ohjelmistojen avulla valtionhallinta on iso juttu. Laatikossa on pari tapaa käsitellä tilaa Reagoi :
Yleisesti ottaen kumpikaan näistä ei ole hieno. Tilan pitäminen ylätasolla on melko yksinkertaista, mutta silloin on suuri määrä sovellustilan siirtämistä jokaiselle sitä tarvitsevalle komponentille.
Vertailun vuoksi, globaalien muuttujien (tai muiden valtion naiiviversioiden) saaminen voi johtaa vaikeasti jäljitettäviin samanaikaisuusongelmiin, mikä johtaa komponenttien päivittymättä odotetulla tavalla tai päinvastoin.
Joten miten tähän voidaan puuttua? Niille teistä, jotka tuntevat Reactin, olet ehkä kokeillut Reduxia, valtion kontti JavaScript sovelluksia. Olet ehkä löytänyt tämän omasta tahdostasi etsimällä rohkeasti hallittavaa järjestelmää valtion ylläpitämiseksi. Tai olet ehkä vain törmännyt siihen lukiessasi JavaScriptiä ja muita verkkotyökaluja.
Huolimatta siitä, miten ihmiset päätyvät katsomaan Reduxia, kokemukseni mukaan heillä on yleensä kaksi ajatusta:
Yleisesti ottaen Redux tarjoaa abstraktion, jonka avulla valtionhallinta mahtuu reaktiivinen Reactin luonne. Lataamalla kaikki tilat Reduxin kaltaiselle järjestelmälle, säilytät puhtaus React. Näin päädyt paljon vähemmän päänsärkyyn ja yleensä sellaiseen, johon on paljon helpompi perustella.
Vaikka tämä ei välttämättä auta sinua oppimaan ClojureScriptiä kokonaan tyhjästä, tässä aion ainakin kertoa joitain Clojure [Script] -tilan peruskäsitteitä. Voit ohittaa nämä osat, jos olet jo kokenut clojurilainen !
Muista yksi Clojure-perusteista, jotka koskevat myös ClojureScript: Oletuksena tiedot ovat muuttumattomia . Tämä on erinomaista kehittää ja taata, että aikataulussa N luomasi on edelleen sama ajanjaksolla> N. ClojureScript tarjoaa meille myös kätevän tavan saada muutettava tila tarvittaessa, atom
konsepti.
atom
ClojureScript on hyvin samanlainen kuin AtomicReference
Java: Se tarjoaa uuden objektin, joka lukitsee sen sisällön samanaikaisuustakeilla. Aivan kuten Java-ohjelmassa, voit sijoittaa mihin tahansa haluamaasi tähän esineeseen - siitä lähtien atomi on atomi viite mihin tahansa.
Kun sinulla on atom
, voit asettaa siihen uuden arvon käyttämällä reset!
toiminto (huomioi funktiossa !
- Clojure-kielellä tätä käytetään usein osoittamaan, että operaatio on tilallinen tai epäpuhdas).
Huomaa myös, että toisin kuin Java, Clojure ei välitä siitä, mitä laitat atom
Se voi olla merkkijono, luettelo tai objekti. Dynaaminen kirjoittaminen, kulta!
(def my-mutable-map (atom {})) ; recall that {} means an empty map in Clojure (println @my-mutable-map) ; You 'dereference' an atom using @ ; -> this prints {} (reset! my-mutable-map {:hello 'there'}) ; atomically set the atom (reset! my-mutable-map 'hello, there!') ; don't forget Clojure is dynamic :)
Reagenssi laajentaa tätä atomin käsitettä omilla atom
. (Jos et ole perehtynyt Reagentiin, tarkista viesti ennen tätä .) Tämä toimii samalla tavalla kuin ClojureScript atom
, paitsi että se laukaisee myös renderöintitapahtumat Reagentissa, aivan kuten Reactin sisäänrakennettu valtion myymälä.
Esimerkki:
(ns example (:require [reagent.core :refer [atom]])) ; in this module, atom now refers ; to reagent's atom. (def my-atom (atom 'world!')) (defn component [] [:div [:span 'Hello, ' @my-atom] [:input {:type 'button' :value 'Press Me!' :on-click #(reset! My-atom 'there!')}]])
Tämä näyttää yksittäisen, joka sisältää sanomalla 'Hei maailma!' ja painike, kuten voit odottaa. Painikkeen painaminen mutatoi atomisesti
my-atom
sisältää 'there!'
. Se käynnistää komponentin uudelleensuunnittelun, minkä seurauksena alue sanoo “Hei, siellä! sen sijaan.
Tämä näyttää riittävän yksinkertaiselta paikalliselle komponenttitason mutaatiolle, mutta entä jos meillä on monimutkaisempi sovellus, jolla on useita abstraktiotasoja? Tai jos meidän on jaettava yhteinen tila useiden alikomponenttien ja niiden alikomponenttien välillä?
Tutkitaan tätä esimerkin avulla. Täällä toteutamme karkean kirjautumissivun:
(ns unearthing-clojurescript.login (:require [reagent.core :as reagent :refer [atom]])) ;; -- STATE -- (def username (atom nil)) (def password (atom nil)) ;; -- VIEW -- (defn component [on-login] [:div [:b 'Username'] [:input {:type 'text' :value @username :on-change #(reset! username (-> % .-target .-value))}] [:b 'Password'] [:input {:type 'password' :value @password :on-change #(reset! password (-> % .-target .-value))}] [:input {:type 'button' :value 'Login!' :on-click #(on-login @username @password)}]])
Isännöimme sitten tätä kirjautumiskomponenttia pääosassa app.cljs
, kuten näin:
(ns unearthing-clojurescript.app (:require [unearthing-clojurescript.login :as login])) ;; -- STATE (def token (atom nil)) ;; -- LOGIC -- (defn- do-login-io [username password] (let [t (complicated-io-login-operation username password)] (reset! token t))) ;; -- VIEW -- (defn component [] [:div [login/component do-login-io]])
Odotettu työnkulku on siis:
do-login-io
toiminto pääkomponentissa.do-login-io
function tekee joitain I / O-toimintoja (kuten kirjautuu palvelimelle ja noutaa tunnuksen).Jos tämä toiminto estää, olemme jo kasassa ongelmia, koska sovelluksemme on jumissa - jos se ei ole, meillä on asynkronia, josta huolehtia!
Lisäksi meidän on nyt toimitettava tämä tunnus kaikille alikomponenteillemme, jotka haluavat tehdä kyselyitä palvelimellemme. Koodin uudelleenkäsittely sai juuri paljon vaikeampaa!
Lopuksi, komponenttimme ei ole enää puhtaasti reaktiivinen —Se on nyt osallisena muun sovelluksen tilan hallinnassa, käynnistää I / O: n ja on yleensä vähän haittaa.
Redux on taikasauva, joka saa kaikki valtiolliset unelmasi toteutumaan. Oikein toteutettuna se tarjoaa tilaa jakavan abstraktin, joka on turvallista, nopeaa ja helppokäyttöistä.
Reduxin sisäinen toiminta (ja sen takana oleva teoria) jäävät jonkin verran tämän artikkelin ulkopuolelle. Sen sijaan aion sukeltaa toimivaan esimerkkiin ClojureScriptin kanssa, jonka pitäisi toivottavasti mennä jonkin verran osoittamaan, mihin se kykenee!
Yhteyksissämme Redux on yksi monista saatavilla olevista ClojureScript-kirjastoista; tämä soitti kehys uudelleen . Se tarjoaa Clojure-ified-kääreen Reduxin ympärille, mikä (mielestäni) tekee siitä täydellisen ilon käyttää.
Redux nostaa sovellustilan, jolloin komponentit ovat kevyet. Reduxified-komponentin täytyy vain miettiä:
Loput hoidetaan kulissien takana.
Korostaaksemme tätä asiaa, tehkäämme uudelleen yllä oleva kirjautumissivumme.
Ensimmäiset asiat ensin: Meidän on päätettävä, miltä sovellusmallimme näyttää. Teemme tämän määrittelemällä muoto tiedoistamme, datasta, joka on käytettävissä koko sovelluksessa.
Hyvä nyrkkisääntö on, että jos tietoja on käytettävä useissa Redux-komponenteissa tai niiden on oltava pitkäikäisiä (kuten tunnuksemme tulee olemaan), ne tulisi tallentaa tietokantaan. Sitä vastoin, jos tiedot ovat komponentin paikallisia (kuten käyttäjätunnus- ja salasanakenttämme), niiden tulisi elää paikallisena komponenttitilana eikä niitä pitäisi tallentaa tietokantaan.
Luodaan tietokannan kattolevy ja kerrotaan tunnuksemme:
(ns unearthing-clojurescript.state.db (:require [cljs.spec.alpha :as s] [re-frame.core :as re-frame])) (s/def ::token string?) (s/def ::db (s/keys :opt-un [::token])) (def default-db {:token nil})
Tässä on muutama mielenkiintoinen asia, joka kannattaa huomioida:
spec
kirjasto että kuvaa miten tietojemme pitäisi näyttää. Tämä sopii erityisen hyvin dynaamiseen kieleen, kuten Clojure [Script].:opt-un
avainsana, joka tarkoittaa 'valinnainen, pätemätön'. (Clojuressa tavallinen avainsana olisi jotain :cat
, kun taas hyväksytty avainsana voi olla jotain :animal/cat
. Karsinta tapahtuu yleensä moduulitasolla - tämä estää eri moduulien avainsanoja ryöstämästä toisiaan .)Milloin tahansa, meidän on oltava varmoja siitä, että tietokantaamme kuuluvat tiedot vastaavat täällä olevia tietoja.
Nyt kun olemme kuvanneet tietomalliamme, meidän on pohdittava miten näkymä näyttää nämä tiedot. Olemme jo kuvanneet miltä näkökulmamme näyttää Redux-komponentissamme - nyt meidän on yksinkertaisesti yhdistettävä näkymä tietokantaan.
Reduxillä emme pääse suoraan tietokantaamme - tämä voi johtaa elinkaareen ja samanaikaisuuteen. Sen sijaan rekisteröimme suhteemme tietokannan osaan kautta tilauksia .
Tilaus kertoo uudelleenkehykselle (ja Reagentille), että olemme riippuvaisia tietokannan osasta, ja jos kyseistä osaa muutetaan, Redux-komponenttimme tulisi tehdä uudelleen.
Tilaukset on helppo määritellä:
(ns unearthing-clojurescript.state.subs (:require [re-frame.core :refer [reg-sub]])) (reg-sub :token ; <- the name of the subscription (fn [{:keys [token] :as db} _] ; first argument is the database, second argument is any token)) ; args passed to the subscribe function (not used here)
Täällä rekisteröimme yhden tilauksen - itse tunnukseen. Tilaus on yksinkertaisesti tilauksen nimi ja toiminto, joka poimii kyseisen kohteen tietokannasta. Voimme tehdä mitä haluamme tuohon arvoon ja mutatoida näkymän niin paljon kuin haluamme täällä; tässä tapauksessa kuitenkin yksinkertaisesti poimimme tunnuksen tietokannasta ja palautamme sen.
On paljon, paljon Enemmän voit tehdä tilauksilla - esimerkiksi määrittelemällä näkymiä tietokannan alaosioista uudelleenhahmonnuksen tiukentamiseksi - mutta pidämme sen nyt yksinkertaisena!
Meillä on tietokanta ja näkemyksemme tietokantaan. Nyt meidän on käynnistettävä joitain tapahtumia! Tässä esimerkissä meillä on kahdenlaisia tapahtumia:
Aloitamme helpommasta. Uudelleen kehys tarjoaa jopa toiminnon tarkalleen tällaiselle tapahtumalle:
(ns unearthing-clojurescript.state.events (:require [re-frame.core :refer [reg-event-db reg-event-fx reg-fx] :as rf] [unearthing-clojurescript.state.db :refer [default-db]])) ; our start up event that initialises the database. ; we'll trigger this in our core.cljs (reg-event-db :initialise-db (fn [_ _] default-db)) ; a simple event that places a token in the database (reg-event-db :store-login (fn [db [_ token]] (assoc db :token token)))
Jälleen, tässä on aika suoraviivaista - olemme määrittäneet kaksi tapahtumaa. Ensimmäinen on tietokannan alustaminen. (Katso, miten se jättää huomiotta molemmat argumentit? Alustamme tietokannan aina default-db
! -Palvelumme avulla.) Toinen on tunnuksen tallentamiseen, kun olemme saaneet sen.
Huomaa, että kummallakaan näistä tapahtumista ei ole sivuvaikutuksia - ei ulkoisia puheluita, ei I / O: ta ollenkaan! Tämä on erittäin tärkeää pyhän Redux-prosessin pyhyyden säilyttämiseksi. Älä tee siitä epäpuhdasta, ettet haluaisi Reduxin vihaa.
Lopuksi tarvitsemme kirjautumistapahtumamme. Sijoitamme sen muiden alle:
(reg-event-fx :login (fn [{:keys [db]} [_ credentials]] {:request-token credentials})) (reg-fx :request-token (fn [{:keys [username password]}] (let [token (complicated-io-login-operation username password)] (rf/dispatch [:store-login token]))))
reg-event-fx
funktio on pitkälti samanlainen kuin reg-event-db
, vaikka siinä on joitain hienovaraisia eroja.
reg-event-db
db
, palautamme sen sijaan kartan, joka kuvaa kaikki vaikutukset ('fx'), joiden pitäisi tapahtua tälle tapahtumalle. Tässä tapauksessa kutsumme yksinkertaisesti :request-token
vaikutus, joka on määritelty alla. Yksi muista kelvollisista vaikutuksista on :dispatch
, joka yksinkertaisesti kutsuu toista tapahtumaa.Kun vaikutuksemme on lähetetty, :request-token
-efektiä kutsutaan, joka suorittaa pitkäaikaisen I / O-kirjautumistoimintomme. Kun tämä on valmis, se lähettää mielellään tuloksen takaisin tapahtumasilmukkaan, mikä täydentää jakson!
Niin! Olemme määrittäneet varastoinnin. Miltä komponentti näyttää nyt?
(ns unearthing-clojurescript.login (:require [reagent.core :as reagent :refer [atom]] [re-frame.core :as rf])) ;; -- STATE -- (def username (atom nil)) (def password (atom nil)) ;; -- VIEW -- (defn component [] [:div [:b 'Username'] [:input {:type 'text' :value @username :on-change #(reset! username (-> % .-target .-value))}] [:b 'Password'] [:input {:type 'password' :value @password :on-change #(reset! password (-> % .-target .-value))}] [:input {:type 'button' :value 'Login!' :on-click #(rf/dispatch [:login {:username @username :password @password]})}]])
Ja sovelluskomponenttimme:
(ns unearthing-clojurescript.app (:require [unearthing-clojurescript.login :as login])) ;; -- VIEW -- (defn component [] [:div [login/component]])
Ja lopuksi, tunnuksen käyttäminen jossakin etäkomponentissa on yhtä helppoa kuin:
(let [token @(rf/subscribe [:token])] ; ... )
Yhdistämällä kaikki yhteen:
Ei jalkoja, ei pakko.
Reduxia käyttämällä (uudelleenkehyksen kautta) erotimme näkymäkomponentit onnistuneesti tilankäsittelyn sotkusta. Valtion vedenoton laajentaminen on nyt kakku!
Redux ClojureScriptissä todella On niin helppoa - sinulla ei ole tekosyitä olla kokeilematta sitä.
Jos olet valmis murtumaan, suosittelen kirjautumista ulos upeat uudelleenkehystetyt asiakirjat ja yksinkertainen toiminut esimerkkimme . Odotan lukevani kommenttisi tästä ClojureScript-opetusohjelmasta alla. Onnea!
Liittyvät: Tilahallinta kulmalla Firebasen avullaRedux-tila viittaa yksittäiseen myymälään, jota Redux käyttää sovellustilan hallintaan. Tätä myymälää hallitsee yksinomaan Redux, eikä siihen pääse suoraan sovelluksesta.
Ei, Redux on erillinen tekniikka mallista, joka tunnetaan nimellä tapahtumien hankinta. Redux sai innoituksensa toisesta tekniikasta nimeltä Flux.
Redux-säilö (tai yksinkertaisesti 'kontti') on React-komponentti, joka tilaa Redux-tilan ja saa päivityksiä, kun tilan osa muuttuu.
Kyllä, Redux tarjoaa puitteet valtionhallinnalle verkkosovelluksessa.
ClojureScript on Clojuren kääntäjä, joka kohdistuu JavaScriptiin. Sitä käytetään yleisesti web-sovellusten ja kirjastojen rakentamiseen Clojure-kieltä käyttäen.