ReactConf AU 2020 | 筆記 — Part 2

Leo Chiu
Starbugs Weekly 星巴哥技術專欄
15 min readDec 15, 2020

前言

之前看了 4 場 ReactConf AU 2020 的演講後,最近又陸續看了幾場,每一場真的都收益良多。但是礙於 medium 寫文章的長度,只能先整理 2 場跟大家分享。在整理了 2 場的演講筆記後,當我在整理第 3 場時,突然跳出通知說 medium 無法儲存太長的文章 😅。

這篇筆記包括兩場演講,分別是 react-final-form 作者 Erik Rasmussen 所演講的 Modern Forms in React,以及 emotion 的維護者 Mitchell Hamilton 帶來的 Building with Monorepos。

如果想看看其他場演講可以點擊以下連結回去看看哦~

Modern Forms in React — Erik Rasmussen

https://www.youtube.com/watch?v=v1JAUiqskiw&ab_channel=ReactConfAU

目前在 react 的世界中,較有名的 form 解決方案像是 formikredux formreact final form,以及較新的 react hook form。這場演講則是由 react final form 的作者 Erik Rasmussen 為我們介紹該套件的特色與相較於其他套件的優勢。

https://youtu.be/v1JAUiqskiw?t=75

以上這一段程式碼有一個 bug,在 useState() 中沒有加上初始值,因此 react 會警告你 <input /> 是一個 uncontrolled input。很簡單地,要解決這個 bug,就是將 useState() 改成 useState('')

https://youtu.be/v1JAUiqskiw?t=281

另外,react 的 form 除了初始值以外,還有另一個更重大的 issue 需要被考慮,那就是 form state,像是 field value、valid/invalid、validation error、active、submit failed、touched、submitting、modified、submit succeeded、submit error 等等。

因此為了解決上述問題,react final form 提供了相對應的解決方案,讓我們不必再自己重新開發 form 的程式。

以下讓我們來看看 react final form 的優勢

smaller bundle size

React final form 在經過壓縮之後的大小僅僅只有 3.3 KB,而其必須使用的 final form 核心的檔案大小有 5.2 KB,所以總共加起來是 8.5 KB。因此,如果是需要追求 bundle size 的專案,可以考慮使用 final form 這個解決方案。

如果你已經使用了 redux form 或是 formik 作為 react form 的解決方案,作者也提供相對應的 migrate 方式。

https://youtu.be/v1JAUiqskiw?t=618

Prevent re-render

通常我們在寫 form 的程式碼時,會將 form 的資料儲存在一個物件中,當物件的資料改變時,整個 form 與每個 field 都會被 re-render,如果 form 的欄位很多的話,將有可能造成效能的問題。

為了解決 re-render 這個問題,react final form 使用訂閱的機制,讓每個 field 只在其相對應的 value 改變時,只 re-render 部分的 component,可以有效地解決 re-render 造成效能低的問題。

右邊灰色圈圈是 re-render 的次數,推薦可以點擊下方連結看看作者的 live coding,大約 5 分鐘左右的時間。

https://www.youtube.com/watch?v=v1JAUiqskiw&ab_channel=ReactConfAU

超過 40 個 example

作者也說,以前他在維護 redux form 時收到了各式各樣他沒想過的需求,因此他收集了這些需求,並將需求轉換成 react final form 相對應的程式碼,甚至有些 example 是 redux form 做不到的。

小結

我們從 npm trend 中可以看到 react final form 相較於其他的套件是較少人使用的,甚至新起的 react hook form 漸漸地超過 redux form 與 react final form 的月下載次數。

https://www.npmtrends.com/formik-vs-react-hook-form-vs-redux-form-vs-react-final-form

再從一些更詳細的資訊比較,星星數目前是 formik 大贏其他的函式庫,而差不多時間釋出的 react final form 僅僅只有 6 千顆星星,在演講中作者其實也有提到這個問題。

https://www.npmtrends.com/formik-vs-react-final-form-vs-redux-form-vs-react-hook-form

最後,如果你看過 formik 與 react final form 的範例程式碼,可以了解到它們都是使用 render props 的方式建構 form,在視覺上不免覺得有點複雜。但是,react hook form 不用 render props,而是用 react hooks 的形式與更少量的程式碼, DX 看上去更佳。此外,短短一年多,其星星數已經成長到 1.6 萬顆,比起 react final form 也許是更優的選擇 (?)

Building with Monorepos — Mitchell Hamilton

Mitchell Hamilton 是 emotion 主要的 maintainer 之一,在這場演講中他為我們介紹在 emotion 這個專案中使用到的 monorepo 技術,他們使用的並非是廣為人知的 Lerna,而是由 ThinkmillAtlassian 共同打造的 Bolt,因為 Bolt 解決了很多使用 Lerna 衍生的問題。

Mitchell 提到使用 monorepo 主要有三個問題,分別為 dependency management、packaging、releasing

👉 Dependency management

Lerna 有一個問題是在所有的 monorepo 下都會有個別的 node_modules 資料夾,而假設兩個 monorepo 都使用到了 react,A module 使用 react 1.11,B module 使用 react 1.12,所以不同版本的 react 就會出現在 A 跟 B 的 node_modules 裡面,這聽起來就不是一件好事。

雖然 Bolt 可以將相同版本的 package 拉到外面,讓 package 不會重複出現在數個 node_modules 裡面。

但是,相同版本的 package 是 Bolt 的不成文規定

也就是說,我們必須清楚每一個 package.json 裡面定義的 dependency 是相同版本。如果在 package.json 裡面不小心使用了不同版本的 package,Bolt 仍然不會幫我們處理在 node_modules 中出現重複 package 的問題。

manypkg

所以,為了解決在不同的 package.json 可能定義不同版本的套件,Mitchell 他們開發了一個工具 — Manypkg

Manypkg 是一個針對 package.json 的 linter,被用於使用到 monorepo 的情況下,可以為我們檢查各個 monorepo 所使用的 package 版本,如此一來,就能夠避免在不同的 pacakage 中使用到不同版本的,使用到不同版本的相依套件。

👉 Packaging

https://youtu.be/Q5Nw3zdpNfM?t=396

Packaging 主要有以上幾個問題,第一個問題「consistent package.json fields」指的是說在 package.json 中可能會有些欄位定義的怪怪的 (不是技術問題,而是人的問題):

{
"name": "@emotion/react",
"main": "dist/commonjs.js",
"module": "dist/emotion.es.js"
}

mainmodule 對應的檔案命名規則不一致,導致閱讀上有點困惑 (比較偏向 minor issue)。

Preconstruct 是另一個 emotion 中使用到的工具,它能夠統一 package.json 一些欄位的規則,讓命名規則變得一致:

{
"name": "@emotion/react",
"main": "dist/emotion-react.cjs.js",
"module": "dist/emotion-react.esm.js"
}

preconstruct — dev and build your code painlessly in monorepos

講者在第二個問題「bundling & bundler configuration」提出相對應的解決方案是 — rollup,他們讓 preconstruct 使用 rollup 作為 bundling的工具,因為 rollup 有很好的 tree shaking,也可以有效地縮小打包後的檔案。

第三個問題「compilation with babel」指的是有時候我們會使用 .babelrc 定義 babel 的設定檔,出問題時可以考慮將檔案變成 babel.config.jsbabel.config.json 就可以解決很多問題 (神奇 🤔)。

第四個問題「multiple entrypoints」指的是有時候我們會從不同的 package 路徑 import 不同的 function,像是以下範例, 呼叫 getEmojiId 會隨機產生三個 emoji id,而 getAwesomeId 則是可以取得某個 emoji 的 id,如果要做到這個件事,preconstruct 也可以幫我們做到這件事。

import getEmojiId from "emoji-uid";import getAwesomeId from "emoji-uid/awesome";

只要像是以下這樣在 package.json 中設定:

"preconstruct": {  "entrypoints": ["index.js", "awesome.js"]}

preconstruct 就會將陣列的第一個位置作為 default 的 package,後面則是相對應的路徑。

第五個問題「developing without having to constantly rebuild」指的是說在開發時,如果有使用到多個 package,在每個 package 的程式碼經過修改使,照理來說應該要重新 build 一次,產生相對應的 /dist 後才能使用修改後的 package。

但是如果每次都要完整重新打包非常麻煩,所以有 preconstruct dev 這個指令,可以幫我們解決 rebuild 這個問題。

👉 Releasing

https://youtu.be/Q5Nw3zdpNfM?t=756

講者提到 releasing 會有以上四個問題,第一個問題「contributors declare their changes」淺而易見地,每個 contributor 都必須在 PR 提到新增了哪些功能。

以及第二個問題「not Git based」指的是如果要基於 git 來管理 releasing 通常會比較麻煩,因為很多時候我們會使用 rebase、squash、merge,如果要基於 git,則開發人員必須擁有豐富的 git 知識,然後可能最後還需要打造一個套件來處理 releasing 的問題。

為此他們打造了一個工具叫做 Changesets

Changesets: A way to manage your versioning and changelogs with a focus on multi-package repositories

在 Changesets 的 repo 中可以看到有不少有名的套件都使用這個工具,像是 emotionFirebase Javascript SDKFormikMobX 等等。

第三個問題「bump dependent packages」指的是套件版本衝突的問題,像是 A module 1.0.0 原本使用 B module 1.0.0,而當 B module 變成 2.0.0 時, A module 在 package.json 中也要同時升級其版本與 B module 的版本。

第三個問題同樣可以被 changeset 解決。

Changeset 當然也可以解決第四個問題「easy to automate releases」,它的流程如下,這邊提到的 automatic 並非是完全的自動化,而是解決比較瑣碎的問題,像是在 publish 之後會有 PR 自動被開啟,並且包含修改描述,最後是由我們來管理 PR。

小結

這場演講的主要內容差不多到這邊,最後還是有一小段總結的部分,就留給大家自行去看囉! 😃

講者很好心地整理了 monorepo guileline,內容包括:

  • Getting start
  • Why monorepo?
  • Thinking in monorepo
  • starter

四個部分,對於剛要開始接觸 monorepo 的人是一大福音。

總結

看完這兩場演講後,讓我有些啟發,首先是 react-final-form 看起來挺完整,但是後來轉眼看到了 react-hook-form,讓我更想嘗試用用看 react-hook-form 😆,就如前文提到 react-hook-form 的語法更為簡潔,而且功能也很豐富,有朝一日需要建構一個大型的 form 時應該會首要評估要不要採用 react-hook-form。

另外是 emotion 的維護者 Mitchell Hamilton 在這場演講中提到了他在 emotion 這個 CSS-in-JS 的套件中所使用的 monorepo 工具及經驗,這幾個工具雖然星星數不多,但是都默默地被廣為人知的套件採用,讓我再次明白到星星數並不能完整表現出一個 open source 的價值。

每個專案都有其適合的套件

ManypkgPreconstructChangesets 這三個套件大家都可以要找時間來玩玩看,讓我們一起站在巨人的肩膀上前進 😃。

分享就到這邊,如果喜歡我的文章可以幫我拍個幾下手,在閱讀文章時如果有遇到什麼問題,或是有什麼建議,都歡迎留言告訴我,謝謝。 😃

--

--

Leo Chiu
Starbugs Weekly 星巴哥技術專欄

每天進步一點點,在終點遇見更好的自己。 Instragram 小帳:@leo.web.dev