感想
近期一直在研究Redux,搞得一頭霧水,上週看了很多文檔,多數講的是數據之間的傳遞和使用方法,感覺機械搬copy相關代碼並無大用,萬一遇到什麼奇葩的項目,還是得另尋道路
上週搞了一直的redux,反覆架構實驗最後都沒有得到太多有用的東西,跟着教程一步一步來,卻沒有搞明白他的核心思想到底是什麼,一直到27號,找到了一些能夠引起我注意的東西,也許不是redux最核心的思想,但值得肯定的是,他在某些React Project with redux 中確實起到了關鍵的作用
上週的周志一直沒能好好寫,也是通過這篇日誌,算是給我的周志一個補充和安慰吧,問題還是在於redux到底是個什麼東西,很早就聽說redux是塊硬骨頭,如大家所說,確實難啃,而且目前看來,毫無味道,簡單的總結一下我是如何去啃這塊骨頭的
總結
你可能不需要 Redux
首先明確一點,Redux 是一個有用的架構,但不是非用不可。事實上,大多數情況,你可以不用它,只用 React 就夠了。
曾經有人說過這樣一句話。
“如果你不知道是否需要 Redux,那就是不需要它。”
Redux 的創造者 Dan Abramov 又補充了一句。
“只有遇到 React 實在解決不了的問題,你才需要 Redux 。”
簡單說,如果你的UI層非常簡單,沒有很多互動,Redux 就是不必要的,用了反而增加複雜性。
- 用戶的使用方式非常簡單
用戶之間沒有協作- 不需要與服務器大量交互,也沒有使用 WebSocket
-視圖層(View)只從單一來源獲取數據
上面這些情況,都不需要使用 Redux。
- 用戶的使用方式複雜
- 不同身份的用戶有不同的使用方式(比如普通用戶和管理員)
- 多個用戶之間可以協作
- 與服務器大量交互,或者使用了WebSocket
- View要從多個來源獲取數據
上面這些情況纔是 Redux 的適用場景:多交互、多數據源。
從組件角度看,如果你的應用有以下場景,可以考慮使用 Redux。
- 某個組件的狀態,需要共享
- 某個狀態需要在任何地方都可以拿到
- 一個組件需要改變全局狀態
- 一個組件需要改變另一個組件的狀態
發生上面情況時,如果不使用 Redux 或者其他狀態管理工具,不按照一定規律處理狀態的讀寫,代碼很快就會變成一團亂麻。你需要一種機制,可以在同一個地方查詢狀態、改變狀態、傳播狀態的變化。
總之,不要把 Redux 當作萬靈丹,如果你的應用沒那麼複雜,就沒必要用它。另一方面,Redux 只是 Web 架構的一種解決方案,也可以選擇其他方案。
設計思想
Redux 的設計思想很簡單,就兩句話。
(1)Web 應用是一個狀態機,視圖與狀態是一一對應的。
(2)所有的狀態,保存在一個對象裏面。
React-Redux
Redux 的作者封裝了一個 React 專用的庫 React-Redux,本文主要介紹它。
這個庫是可以選用的。實際項目中,你應該權衡一下,是直接使用 Redux,還是使用 React-Redux。後者雖然提供了便利,但是需要掌握額外的 API,並且要遵守它的組件拆分規範。
UI 組件
React-Redux 將所有組件分成兩大類:UI 組件(presentational component)和容器組件(container component)。
UI 組件有以下幾個特徵。
- 只負責 UI 的呈現,不帶有任何業務邏輯
- 沒有狀態(即不使用this.state這個變量)
- 所有數據都由參數(this.props)提供
- 不使用任何 Redux 的 API
下面就是一個 UI 組件的例子。
const Title =
value => <h1>{value}</h1>;
因爲不含有狀態,UI 組件又稱爲"純組件",即它純函數一樣,純粹由參數決定它的值。
容器組件
容器組件的特徵恰恰相反。
- 負責管理數據和業務邏輯,不負責 UI 的呈現
- 帶有內部狀態
- 使用 Redux 的 API
總之,只要記住一句話就可以了:UI 組件負責 UI 的呈現,容器組件負責管理數據和邏輯。
你可能會問,如果一個組件既有 UI 又有業務邏輯,那怎麼辦?回答是,將它拆分成下面的結構:外面是一個容器組件,裏面包了一個UI 組件。前者負責與外部的通信,將數據傳給後者,由後者渲染出視圖。
React-Redux 規定,所有的 UI 組件都由用戶提供,容器組件則是由 React-Redux 自動生成。也就是說,用戶負責視覺層,狀態管理則是全部交給它。
connect()
React-Redux 提供connect
方法,用於從 UI 組件生成容器組件。connect
的意思,就是將這兩種組件連起來。
import { connect } from 'react-redux'
const VisibleTodoList = connect()(TodoList);
上面代碼中,TodoList
是 UI 組件,VisibleTodoList
就是由 React-Redux 通過connect
方法自動生成的容器組件。
但是,因爲沒有定義業務邏輯,上面這個容器組件毫無意義,只是 UI 組件的一個單純的包裝層。爲了定義業務邏輯,需要給出下面兩方面的信息。
(1)輸入邏輯:外部的數據(即state對象)如何轉換爲 UI 組件的參數
(2)輸出邏輯:用戶發出的動作如何變爲 Action 對象,從 UI 組件傳出去。
mapStateToProps()
mapStateToProps
是一個函數。它的作用就是像它的名字那樣,建立一個從(外部的)state
對象到(UI 組件的)props
對象的映射關係。
作爲函數,mapStateToProps
執行後應該返回一個對象,裏面的每一個鍵值對就是一個映射。
mapDispatchToProps()
mapDispatchToProps
是connect
函數的第二個參數,用來建立 UI 組件的參數到store.dispatch
方法的映射。也就是說,它定義了哪些用戶的操作應該當作 Action
,傳給 Store
。它可以是一個函數,也可以是一個對象。
如果mapDispatchToProps
是一個函數,會得到dispatch
和ownProps
(容器組件的props
對象)兩個參數。
組件
connect
方法生成容器組件以後,需要讓容器組件拿到state
對象,才能生成 UI 組件的參數。
一種解決方法是將state
對象作爲參數,傳入容器組件。但是,這樣做比較麻煩,尤其是容器組件可能在很深的層級,一級級將state
傳下去就很麻煩。
React-Redux 提供Provider
組件,可以讓容器組件拿到state
。
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp);
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
參考文檔
個人項目
ReactBlogwithRedux