今天的目標是用 function 讓資料模型「動」起來。想像一個 function 串著一個 function ,把資料揉揉捏捏,經過一道道工序的處理最後變成我們想要的樣子,聽起來就很棒對吧 \(^∀^)メ(^∀^)ノ
我們同樣用課程名稱的輸入來舉例
ui event
ui event
,我們掛在 event hook
的 function
就會被呼叫,這時候我們要做的就是把React event
(例如輸入改變事件) 轉換成我們剛剛設計的 domain type
(課程名稱)Atom
來管理我們的 domain type
(Atom
來自 Jotai)Atom
所管理的 domain type
被改變都會讓有使用這個 domain type
的 component
被重新渲染,使得更新後的 domain type
能轉換成參數傳遞到所屬的子元件,完成畫面的更新。實際上不只是課程名稱,整個表單的驗證都只需要遵循步驟一到步驟四就可以完成 !
如果你不熟悉 React 還有 Atom,可能會覺得有點抽象,不過這很正常,沒有關係,
多看看多寫寫就會了
把上面的文字轉換後,我們可以用以下兩個 function 來組成 function A
type getInputString = (event:ChangeEvent) => string // 從輸入事件取出字串
type courseNameOf = (input:string) => CourseName // 把字串傳換成先前定義的資料模型
可是 ... 該怎麼把這兩個 function 組合在一起做呢 ?
照一般原生寫法我們會寫成這樣
const input = getInputString(event)
const name = courseNameOf(input)
看起來沒有甚麼問題,可是如果想要組合的 function 變多會發生甚麼事呢 ?
const temp1 = add7(3)
const temp2 = exp2(temp1)
const temp3 = sub2(temp2)
const result = mul5(temp3)
無意義的變數命名好累好麻煩(T▽T)
const result = mul5(sub2(exp2(add7(3))))
省略掉變數以後,閱讀順序怪怪的不太自然,還要加上很多括號,好像更糟糕了 (T▽T)
為了解決以上問題,pipe 就出現啦 ! 有了 pipe 以後,多個 function 的組合看起來會像這樣
import { pipe } from 'effect/Function'
const result = pipe(1, add7, exp2, sub2, mul5)
課程名稱的範例則會像這樣
const courseName = (event:ChnageEvent)=>pipe(event, getInputString, courseNameOf)
我們之後會常常用到這種語法,克服習慣問題以後,會發現看起來很舒服唷 (^ω^)
在使用 pipe 的時候常常會發現如下圖 calculate function 的輸入參數等於 pipe 的第一個參數的情形
const calculate = (input:number)=>pipe(input, add7, exp2, sub2, mul5)
這時候我們可以用 flow 來取代重複出現的 input,看起來又更簡潔了!
import { flow } from 'effect/Function'
const calculate = flow(add7, exp2, sub2, mul5)
講完 pipe 跟 flow 聯想到兩個酷酷的術語跟大家分享。
flow 或 pipe 和其他的範例 function 不太一樣,這是因為它們把 function 當作輸入或是輸出了,這代表他們是 higher order function
Higher-order function
指某個 function 滿足以下任一條件
- 把其他 function 作為參數傳入
- 把其他 function 做回回傳值傳出
在 js 語言中 function 是一等公民,因為它符合以下定義
Functions are first-class citizen
指在某個語言中,function 像其他物件一樣可以作為變數儲存、作為參數傳遞、作為 function 的回傳值回傳。
在寫 JS 的時候我們可以把 function 當作參數丟進去一個 higher order funciton 產生另一個 function,真是太有趣了,對吧 ..... ?