Fork me on GitHub
#clojure-norway
<
2023-11-22
>
slipset06:11:36

God morgen! Destination Hællæwood idag :wind_blowing_face: 🏄

🪵 6
🧇 4
🌭 4
🥶 4
slipset06:11:03

Sånn tanke på morgenkvisten. I tillegg til alt annet som er feil med useEffect, så ser jeg gjerne bruken av den hos oss som:

useEffect(() => { masse lavnivåkode })
Det gjør at jeg må fin lese lavnivåkoden for å forstå hva som skjer. Det liker jeg ikke. Hvis det hadde stått noe sånt som: useEffect(loadCrapFromServer) eller til og med useEffect(() => {loadCrapFromServer(withSomeArgs)}) så hadde det vært enklere for meg å lese koden.

slipset07:11:17

Og, selvom jeg ikke har lest boka, så har jeg inntrykk av at Grokking Simplicity setter fingeren på problemet mitt (hvis ikke så attribuerer jeg det til Eric Normand uansett). Problemet er at man har kode på ulike abstraksjonsnivå rett ved siden av hverandre. i React komponenter driver man med rendring av dom. Da bør tingene man gjør handle om det, og ikke f.eks http statuskoder eller mapping av json til domeneobjekter.

❤️ 4
🎯 2
💯 2
oddsor08:11:08

Jeg gjør for tiden et forsøk på å få overtalt kolleger om at dette:

useEffect(lastGreierFordiJegTilfeldigvisDukketOppIDOM,[])
er et stusselig pattern. Opplever det som en tung skute å snu enn så lenge, men det skal nok ordne seg! Argumenter for at man skal drive med dette er tross alt ganske svake 🤪

oddsor08:11:03

Når jeg startet min IT-karriere så hadde react-miljøet tilstandshåndtering og side-effekter via redux, som tross sine begrensninger må kunne sies å ha fasilitert bedre frontend-kode enn dette? Skjønner liksom ikke hvor det ble av, men det henger sikkert sammen med at React har prøvd å gjøre seg selv til å handle om mer enn bare et rendering

Hagenek09:11:14

For et par uker skriven refaktorerte jeg en useEffect som inneholdt 100 linjer kode, etter å ha delt den opp i funksjoner fant jeg i ut at nesten halvparten ikke var nødvendig, og endte i tillegg opp med å halvere antall renders i appen. Leser faktisk Grokking Simplicity også nå, og bare den actions, calculations og data inndelingen er en utrolig nyttig tankemåte. Her er min erfaring og tanker som noob som kom inn i web-verdenen akkurat når Redux var stort men også på vei ut: Redux kom(mer) med sine egne problemer: 1. Bratt læringskurve 2. Stort krav til arbeidsminne (actions, reducer, original komponent generelt mye kode). I tillegg tror jeg ofte react komponenter starter sitt liv med en enkel useEffect, også utvikler prosjektet og koden seg til å ende opp med 3-4 stykker. Det vel react query som har erstattet mye redux? I tillegg er det spennende ting man kan gjøre med kombinasjonen react-router + react-query.

cjohansen11:11:10

@UDB2Q0W13 nå er ikke jeg noen hooks-ekspert, men jeg er overbevist om at redux la opp til dramatisk bedre arkitektur, ja

Hagenek11:11:44

@UDB2Q0W13 Hvordan prøver du å overtale dem? Hva er alternativet du skisserer?

oddsor14:11:00

Det skal sies at jeg personlig ikke er 100% bestemt på det beste alternativet da, men gitt at jeg tenker at redux også var et fullgodt alternativ, så argumenterer jeg gjerne som om det er dit vi burde gå. Og så er kanskje ikke alle argumenter like gjennomtenkte! Men siden du spør så kan du få mange ustrukturerte tanker 😅: • Komponenter burde i minst mulig grad være ansvarlig for å gjøre nettverkskall eller andre side-effekter. Det gjør det vanskelig å sette opp visuelle frontend-tester i feks Storybook (@U9MKYDN4Q sin Javazone-presentasjon viste jo at man kan putte nesten hele applikasjonen med ulike tilstander i Portfolio) • Hvis komponenter må være ansvarlig for side-effekter så burde de isåfall dispatche en eller annen event/action (da kan den frakobles “servicelaget” i frontenden, og trenger ikke å vite om det heller) • Hvis komponenter skal ha data så burde de komme inn via props (ikke useContext, jeg er ørlite mer pragmatisk til useState) Men det viktigste med reglene over må vel være hva man oppnår, og da tenker jeg at man feks oppnår • Enklere testing av visuelle endringer (kan laste nesten hele appen i feks Storybook i stedet for å måtte trykke seg rundt for å finne riktig tilstand for å sjekke endringen) • Enklere oversikt over hva som faktisk skjer i applikasjonen (Redux hadde et visuelt utviklerverktøy som viste actions som ble fyrt av i appen og hva slags tilstand man endte opp i) • Enklere å flytte rundt på komponenter i DOM (må ikke sørge for å også flytte rundt på “contextprovidere” og lignende som kanskje ligger i en “branch”, men ikke en annen) • Lavere skuldre med at man har mer kontroll over når ting skjer (føler at useEffects kan trigge litt når som helst avhengig av hvor raskt et nettverkskall skjer i prod vs lokalmiljø hvis man er skikkelig uheldig. useEffects trigger to ganger med vilje i development-mode for å sørge for at man ikke bruker de helt feil) • Man kan lettere teste forretningslogikk i action->state når business-logikken ikke trigges via en effekt i en react-komponent

Hagenek09:11:24

Litt artig eksempel på noe jeg mener er dårlig design, men som kanskje ikke er det? Er dette kanskje noe som uansett blir fanget opp ved enhetstester osv. (Jeg fanget det jo opp via og teste koden min via REPL-et, så kanskje ikke så stort problem) Biblioteket buddy-hashers har en funksjon verify. Her er doc-stringen:

buddy.hashers/verify
[attempt encrypted]
[attempt encrypted {:keys [limit prefered], :as options}]
Check if a unencrypted password matches with another encrypted password. Analogous to check with different call signature. Prefer this method over check.
Altså: ingen info om hva den returnerer. Det den faktisk returnerer er et map {:verified boolean, :updated boolean }, som evaluerer til true i Clojure. Noe som vil si at kode som (and user-exists (verify "user" "pw")) vil returnere true, noe som kan sies å være litt problematisk mtp. sikkerhet.

😬 2
cjohansen09:11:11

Det er vel ikke direkte dårlig design, men det er definitivt en dårlig docstring.

👍 2
cjohansen09:11:45

Jeg har ikke tenkt lenge på dette, men siden prisen for å tråkke feil med denne er så høy så ville jeg nesten vurdert å få den til å kaste en exception når ting ikke matcher 😅

✌️ 2
augustl13:11:55

jeg ser et mønster i min kode. Noe smart man kan gjøre her? Å lage en hjemmelaget if-let-seq eller noe sånt blir vel overkill og vel så det

cjohansen13:11:18

Kan bare gjøre (if-let [children (seq (get x k))] ,,,)

augustl13:11:31

ah, doh, det er sant! Takk 😄

cjohansen13:11:46

Evt, om du ikke vil konvertere feks et set til en seq: (if-let [children (not-empty (get x k))] ,,,)

cjohansen13:11:20

not-empty funker på collections, strenger, maps og konverterer tomme verdier ("", [], {}, #{}) til nil

👌 5
til 1
augustl13:11:53

not-empty ender interessant nok opp med å gjøre en (seq) åkke som under panseret, men man får den opprinnelige collectionen tilbake

augustl13:11:10

en annen artig sak: (->> xs (reduce #(..) {}) (map ...) ...) , eller (->> (reduce #(..) {} xs) (map ...) ...) ?

cjohansen13:11:29

kommer an på xs

cjohansen13:11:10

Hvis faktisk xs => 2, hvis litt lenger navn/map-oppslag osv => 1. Det er litt sånn gefühl-sak for meg

augustl13:11:37

xs er en slags navngitt sak ja, og ikke en generisk xs

cjohansen13:11:38

Men jeg unngår stort sett threadinger på én linje når flere av elementene har parametere.

augustl13:11:50

sant, den “ekte” koden har den over flere linjer

cjohansen13:11:21

den funksjonen er for lang til å inlines 😅

cjohansen13:11:46

Her klarer jeg ikke lett å se hva som er fortsettelsen på threadingen, da ville jeg valgt en annen løsning

augustl13:11:04

la meg jukse litt

augustl13:11:11

men ja, det skjer en del greier inni der ja, kanskje den skal få et navn

cjohansen13:11:11

Feks: Trekke ut reduceren som en funksjon eller heller bruke loop

cjohansen13:11:37

Evt trekke ut hele reducen som en funksjon, så blir threadingen litt mer leselig

cjohansen13:11:06

Det hadde fungert bedre om reducen var det siste steget i threadingen

augustl13:11:00

samtidig er det litt fint å ha alt på ett sted akkurat her synes jeg, siden den faktisk er litt “complected” og ting vet litt om hverandre innenfor og utenfor threadinga

augustl13:11:05

(å putte namespaced keys på “andres” data er clojure nirvana)

augustl13:11:20

kanskje kompleksiteten til den funksjonen er et lite hint om at den bør skrives om litt!

augustl13:11:32

er jo mye greier jeg tvinger mine stakkars kollegaer til å ha i hodet samtidig her