
自己認為學會一項新技能之後最興奮的就是怎麼運用到專案了,這篇會用每一個網站都需要處理的"串接 API "為例。從發 request 去要 API 資料一直到拿完資料並整型成 UI 想要得樣子,這中間有可能發生的錯誤實在太多了,所以導入 Either Monad 我覺得非常適合。
假如今天 API 回傳資料如以下
// response
{
  data: [ 
    { 
      email: 'foo@example.com', 
      'user-name': '34Cindy', 
      phone: '#465-876?'
    },
    { 
      email: 'foo!example?dd', 
      'user-name': '3-jsindy', 
      phone: '465-876-998'
    },
    { email: 'foo@example', 
      'user-name': 'Hindy', 
      phone: '46587676'
    }
  ]
}
我們的目標是要由前端 transform 成以下,最後再 render 到 DOM 上面
{
  data: [ 
    { 
      email: 'foo@example.com', 
      userName: 'CINDY', 
      phone: '465876'
    },
    { 
      email: 'foo@example', 
      userName: 'JSINDY', 
      phone: '465876998'
    },
    { email: 'foo@example', 
      userName: 'HINDY', 
      phone: '46587676'
    }
  ]
}
可能會用 map 然後
/*
 Part A 
*/
// ... 代表簡略 
response.map(list => {  
  // list key 變 camelCase
  ...
  // 一個一個 regex  
  let newEmail = list.email.match(regex) ...  
  let newName = list.userName.match(regex) ...  
  let newPhone = list.phone.match(regex) ...   
  return `${newName} / ${newEmail} / ${newPhone}` 
})
然後另外要考慮 Edge Case


(↑ 有考慮 Edge Case,爆錯但可以引導使用者去別的地方)
/*
 Part B
*/ 
fetch(url).then((response) => {   
  if (response.ok) {     
    return response.json();   
  } else {   
    // 處理錯誤 1   
  } }) 
.then((res) => {  
  if(list.length > 0){    
    response.map(list => {    
      // Part A  
      // Part A 也要處理錯誤
    })  
  } else {   
    // 空值要做的事 
  }   
.catch((error) => {   
  // 處理錯誤 2 
});
我相信大家跟我一樣很熟悉以上寫法,這一包 Function 只適用當下 Page / Component,程式裡面也參雜了許多判斷式去判斷各種不同情況 (是不是空值、API 有沒有錯、API 回傳值是不是想要的格式等等)
讓我們先想一下需要步驟
user_name 統一變 camelCase userName
以上每個步驟都有可能有兩種結果

若從頭到尾都很順利,那就是一路運算下去
若中間突然有一 Failed,例如 3. validate data,就會跳過後面步驟直接顯示客製化錯誤訊息
若一開始就 Failed,同理可證,會略過接下來所有步驟直接顯示客製化錯誤訊息
下面會是程式碼大概的樣子,裡面有很多 utilities 但這些都是可以不斷 reuse 的,例如 getProp、checkNoDataState、handleError... 等,程式碼也相當好閱讀與理解,這就是 FP 吸引人的地方吧!
const validateData = data =>
    pipe(
    isEmail('email'),
    isPhoneNumber('phone'),
    isName('userName'),
)(data[0]).map(() => data);
/**
 * Validates & Transforms Data
 *
 * @param {ResponseState} response
 * @return {ResponseState}
 */
function transform (response) {
    return Box(response)
    .chain(getProp('data'))
    .chain(checkNoDataState(x => x.length === 0))
    .chain(validateData)
    .map(transformData)
    .then(
        (transforme) => ({
            ...response,
            data: transforme
        }),
        handleError(response)
    );
}
這邊有 hardcore-functional-js 課程提供串接 weather API 資料原始碼可以參考 ~~
如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您
歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。