ReactJS
React vs Svelte 開發體驗:子元件暫時更新父層資料
當程式規模慢慢變大時,為了管理與維護會將大元件拆分成許多小元件,在父子元件之間傳遞資料是開發時很常見的。但在傳遞資料上,React 跟 Svelte 沒什麼區別,同樣都是將資料傳入元件的 attribute 中,子元件透過 props 來獲得父層傳入的資料。所以我們這章舉一個比較複雜一點的例子:讓子元件可以暫時更新資料,但如果父元件的資料更新時,子元件也要更新為父元件元件的值。我們將透過範例比較兩者在開發體驗上的差異。
開發體驗
開發者在使用工具、框架、語言、API、系統或流程時,整體的感受、效率、流暢度與滿意度。
使用情境
當你在開發表單、步驟流程或是可編輯區塊時,子元件常會需要「暫時修改」父元件提供的資料。例如使用者輸入值,但尚未儲存前,這些變更應該是本地的。這時候就需要讓子元件在不直接修改父層狀態的前提下,保有一定程度的控制權。
React:需要手動管理狀態同步
React 採用單向資料流設計,而且我們無法直接修改 props
。在子元件想要暫時更新資料時,必須透過 useState
加上 useEffect
來同步 props
的變化 (demo)。
ParentComponent.js
1 | import React, { useState } from 'react'; |
ChildComponent.js
1 | import React, { useState, useEffect } from 'react'; |
在這個範例中:
- 子元件透過
useState
創建一個本地的localCount
,用來「暫時更新」畫面上的計數值。 - 當父元件的
count
改變時,useEffect
會同步更新localCount
。 - 子元件的操作不會影響父元件的實際資料。
Svelte:直覺操作、語法簡潔
Svelte 允許我們在子元件中直接修改 props,而不需要額外建立本地狀態。這種語法大幅簡化了開發流程,對開發者非常友善 (demo)。
Parent.svelte
1 | <script> |
Child.svelte
1 | <script> |
- 無需
useEffect
或本地狀態。 - 可直接操作傳入的
count
,操作簡單明瞭。 - 預設變更僅限於子元件本地,不會同步到父層(除非你明確綁定
bind:
或使用事件)。
以上 React 與 Svelte 兩個例子功能相同,但 Svelte 的語法設計,讓開發者可以更快速專注在邏輯本身,而非額外的狀態管理機制。
延伸閱讀
React vs Svelte 開發體驗:雙向資料綁定
資料綁定 (Data Binding) 是連結應用程式資料 (Application) 與使用者介面 (UI) 的方式或機制。React 是使用單向資料綁定 (one-way data flow),需手動呼叫 setState
來更新狀態;而 Svelte 使用 bind:
指令來達成雙向資料綁定。我們將透過範例來了解兩者在開發體驗上的差異。
開發體驗
開發者在使用工具、框架、語言、API、系統或流程時,整體的感受、效率、流暢度與滿意度。
使用情境
當你在開發表單、表單輸入與使用者互動或即時資料顯示,需要反向更新狀態變數,並即時顯示給使用者 (e.g. 動態價格計算、表單即時驗證、即時預覽)。
React:單向資料綁定
React 採用單向資料流 (one-way data flow),透過手動呼叫 setstate
來更新狀態。
1 | import { useState } from 'react'; |
useState
宣告狀態變數text
,初始值為空字串。value={text}
綁定輸入框顯示值。onChange
在輸入變動時更新text
。
Svelte:直覺式雙向綁定
Svelte 提供了 bind:
指令,簡化雙向資料綁定,讓變數與元件保持同步。
1 | <script> |
- 使用
let
宣告響應式 (reactivity) 變數text
。 bind:value={text}
自動雙向綁定輸入值與變數。- 使用者輸入會更新
text
,變數更新也會同步回輸入框。
哲學
React 與 Svelte 在資料綁定上的差異,反映了各自的框架哲學:
- React 注重控制與可預測性,適合處理複雜應用邏輯與資料流。
- Svelte 注重簡潔與開發效率,讓使用者開發時更貼近原生思維。
延伸閱讀
React vs Svelte 開發體驗:狀態管理
在管理資料狀態上,React 採用的是 useState
Hook,而 Svelte 則是直接使用變數,使用起來更直觀。我們將透過範例來了解兩者在開發體驗上的差異。
開發體驗
開發者在使用工具、框架、語言、API、系統或流程時,整體的感受、效率、流暢度與滿意度。
React 狀態管理:使用 useState
Hook
React 讓元件可以擁有內部狀態的方式是透過 useState
Hook 來完成。
1 | import { useState } from 'react'; |
useState(0)
:初始化count
為 0。setCount
:更新count
的函式,每次按鈕點擊讓值加 1。- 透過 React 的 Virtual DOM,變化的值會觸發元件重新渲染。
非同步更新注意事項
React 的狀態更新是非同步的,因此如果在同一函式中連續呼叫多次 setCount(count + 1)
,可能無法即時反映最新的狀態。建議改用以下方式來避免此問題:
1 | setCount(prevCount => prevCount + 1); |
這樣可以保證邏輯永遠是基於最新狀態更新。
Svelte 狀態管理:響應式變數
Svelte 採用一種更直覺的狀態處理方式。它不需要額外的 Hook,直接使用變數即可自動觸發畫面更新。
1 | <script> |
- 使用
let count = 0
宣告變數。 - 直接對
count
進行操作,例如count++
,Svelte 就能自動感知變化並更新 DOM。 - 這是透過 Svelte 的 編譯時響應式系統 (reactivity system) 實現的,完全不需要 Virtual DOM。
Note: React.js 使用 Virtual DOM 來追蹤是否元件需要被更新;而 Svelte 沒有使用 Virtual DOM 而是直些更新在 DOM 上。
React vs Svelte 狀態更新差異
功能面向 | React | Svelte |
---|---|---|
語法複雜度 | 使用 Hook,語法稍多 | 直覺操作變數,語法簡潔 |
DOM 更新機制 | Virtual DOM 比對再更新 | 編譯階段生成 DOM 操作 |
狀態更新方式 | 必須使用 setState / setCount | 直接修改變數 |
更新非同步處理 | 是,需注意資料一致性 | 否,變數改變即更新 |
可讀性與維護性 | 中等 | 高 |
如果追求極簡語法與優化效能,Svelte 提供了非常流暢的開發體驗。而 React 則擁有強大的生態系與社群支持。
延伸閱讀
何時該使用 React Redux
今天這章會討論什麼時候要把資料放在 Redux 裡面,什麼時候要將資料放在 React 的 components 裡。
React 將 components 分成 Presentational Components (展示組件) 與 Container Components (容器組件)。
Presentational Components
Presentational Components 主要負責 UI 的部分,通常不會有複雜 application 狀態管理的資料,通過會由 props 傳入無狀態 (stateless) 的資料,像是:
- 跟 UI 相關的資料 e.g. 顏色的使用
- 只有在必要時才擁有自己的狀態 e.g. 下拉式選單的狀態
- 需要手動建立新的東西 e.g. new post 的資料儲存
Container Components
Container Components 主要負責 application 的狀態,通常會使用 Redux 來管理,然後在 render 的時候會資料傳給 Presentational Components,通常會儲存有狀態的資料:
- application data flow
- 使用者相關的資訊 e.g. 最喜歡的顏色
Compare
特性 | Presentational | Container |
---|---|---|
主要用途 | UI render | application state |
狀態 | 無狀態或是透過 props 獲得狀態 | 通常有狀態,透過 Redux 管理 |
類別 | smart | dumb |
儲存類型 | 儲存複雜的東西 | 儲存簡單的東西 |
Reference
React Redux Example App 範例程式碼
前一篇介紹 Redux 是參考 Flux 的架構而設計的,這篇要用 Redux 來寫一個簡單的 React Counter App。
在 Ubuntu 18.04 上安裝 Docker CE再提醒一下,React 跟 Redux 的差別是:React 是一個 front-end library;Redux 是一個架構,可以不用跟 React 一起使用,也可以跟 Vue 或 Angular 搭配。
Counter App
Install
建立一個 React App 然後安裝 Redux:
1 | npx create-react-app counter-app |
Actions
建立 Redux 的 Action,Action 一定要回傳 type 讓 Reducer 去做對應的資料更新,Middleware 會在送到 Reducer 之前執行。
1 | // actions.js |
Reducer
建立 Redux 的 Reducer,Reducer 不能修改原本的 state,必須複製一份,並且回傳最後 state 應該要的資料。
1 | // reducer.js |
Store
建立 Redux 的 Store,用來儲存 state 的資料
1 | // store.js |
Counter
建立 React Counter Element,而且注意這邊需要將 Action 傳入 Dispatcher 來讓後續的 Reducer 來更新狀態。
1 | // Counter.js |
App
最後 react-redux library 透過 Provider Component 來跟 React 串接,Provider 會提供 Store 的資料。
1 | // App.js |
Reference
Flux 跟 Redux 之間的關係
Flux 和 Redux 都是用來管理 application 狀態的架構,差異是不同的設計理念和實做方式:
Flux
Flux 是 Facebook 提出的架構模式,用於解決複雜的 data flow 問題,Flux 的架構如下:
- Action: 描述 application 的事件或行為。
- Dispatcher: 分發 Action 給 Store。它是 Flux 架構中的中央樞紐。
- Store: 儲存 application 狀態和邏輯。每個 Store 負責 application 一部分狀態。
- View: 展示 application UI,並且可以根據 Store 的變化來更新。
Flux 的 data flow 是單向的,從 Action 到 Dispatcher,再到 Store,最後到 view。
Redux
Redux 參考並簡化 Flux 管理狀態概念,Redux 的架構如下:
- Action: 與 Flux 中的 Action 類似,用於描述 application 的事件或行為。
- Reducer: 是一個 function,負責根據 Action 來更新 application 的狀態。Redux 中沒有 Sispatcher,取而代之的是 Reducer。
- Store: 儲存 application 的狀態。Redux 中只有一個單一的 Store,與 Flux 有多個 Store 不同。而且 Redux 只能透過 Action 可以修改 Store 的資料。
- Middleware: 用於處理異步操作或其他事件 e.g. Error Handling。
Redux 的 data flow 也是單向的,並且強調使用 function 來更新狀態。
Redux 使用單一集中狀態的物件,並以特定的方式進行更新。當你想要更新狀態時(e.g click event),會創建一個 Action 並由某個 Reducer 處理。Reducer 會複製當前狀態,且使用 Action 中的資料進行修改,然後返回新的狀態。當 Store 更新時,可以監聽事件並更新
Redux 跟 React 的差別是:React 是一個 front-end library;Redux 是一個架構,可以不用跟 React 一起使用,也可以跟 Vue 或 Angular 搭配。這篇是一個 React Redux 的範例:
React Redux Example App 範例程式碼Compare
- Redux 使用單一的 store: 與 Flux 在中多個 Store 中定位狀態信息不同,Redux 將所有內容保存在一個地方。在 Flux 中,可以有許多不同的 store。Redux 打破了這一點,強制使用單一 global Store。
- Redux 使用 reducers: Reducers 是以不更動原本資料的方式來更新資料。在 Redux 中,行為是以可預測的,因為所有的改動都要經過 Reducer,且每一次的改動只會更新一次 global Store。
- Redux 使用 middleware: 由於 Action 和資料以單向方式流動,我們可以透過 Redux 增加 middleware,並在資料更新時加上客製化的行為 e.g. Log or Catch Error。
- Redux decouple Action 與 Store: 建立 Action 時不會向通知 Store 任何東西,反而是回傳 Action 物件;Flux 的 Action 則會直接修改 Store。