原罪一:Props & onChange 的原罪 。「props & onChange 介面規範」它不是一個典型的「程式介面規範」。原罪二:Controller 和 View 融為一談。 ...
-
Props & onChange 的原罪 。「props & onChange 介面規範」它不是一個典型的「程式介面規範」。
- 當你拿到一個可視組件的 ref,卻沒有類似
setProps()
這樣的方法來改變其props
,你只能在 render() 方法中,通過 jsx 語言來設置其props
。這意味著父元素必須保存並維護這個 props 對應的值,而更多時候,父容器只是想一次性的設置一個值,然後就走,讓以後的事情交給子元素自己去維護。當然,你也可以通過 ref 來獲取子元素的應用,然後調用其成員方法來達到一次性改變屬性的目的,但又會帶來其他問題,請看下一條。 - 在大多數客戶端框架里,有一個視圖類
ComponentA
,compA = new ComponentA()
,compoA
即是對這個可視組件的表示。而,在 React 世界里,compA.render()
返回的結果(結果的結構參見下圖),才是這個可視組件的表示。這種不同帶來的後果是,我們很難對已經渲染過的節點進行後續更改。當然 React 提供了React.cloneElement(element,props)
介面來改變,但是這個介面怎麼看都不像一種正派的介面,而像是。。。hack
。因為這種改變,其他人時候很難看懂的。換句話說,我們不是通過一個明確定義的介面,而是“隨性的”,“肆無忌憚”的修改了記憶體。 -
React 用
props & PropType
的方式重新定義了一種介面規範,定義了一個組件的輸入和輸出。輸入props
一般是string
,boolean
,number
,object
;輸出參數一般是function
。PropType
定義了每個props
的取值範圍、 是否必填等信息。這整個一套機制是跟面向對象語言里的interface
的功能是高度重合的,也就是說,React 重新發明瞭輪子。並且這個輪子有自己很大的局限性:他不是能自表達的。什麼意思呢?在典型的面向對象語言里(以 java 為例), 假設有一下代碼:1
2
3
4
5
6inteface IRunable{
run();
getSpeed();
}
function clone(IRunable){}這段代碼里的
clone
函數的第一個參數必須滿足IRunable
介面,否則編譯器會拋出編譯錯誤。但是,切換到props
介面規範,假設我們定義了一個對象的介面:1
2
3
4
5
6
7
8
9
10
11const SomePropType = PropType.shape({
name: PropType.string.isRequired,
uuid: PropType.stirng.isRequired,
onChange: PropType.func.isRequired,
});
function clone(element){}. // element 是一個必須滿足 SomePropType 介面的元素。
const AnotherPropType = PropType.shape({
elment: ???how??? // 一個必須滿足 SomePropType 介面的元素。
})你沒辦法定義一個
clone
方法,限制它的第一個參數必須是滿足某個PropType
定義的組件對象;甚至,你也沒辦法定義一個介面AnotherPropType
,使得它的某個屬性滿足SomePropType
。
- 當你拿到一個可視組件的 ref,卻沒有類似
- Controller 和 View 融為一談。React 本身是一個 View 層的工具,他最擅長的是把數據
render()
成 dom 元素。在 Android SDK 里,有一個東西跟它的功能很相似: layout xml。但是在 React 的設計理念里,沒有對 view 和 controller 的明確的概念的區分。我們可以在 Component 里做 view 層該做的事情(根據給定數據顯示界面),也可以做 controller 應該做的事情(處理交互動作、處理網路請求)。以至於有很多博客來“教” React 的開發者來明確分這兩個概念,參考博客Presentational and Container Components。當然你可以說,這是開發者自身素質和抽象能力的體現,但是作為一個使用量如此巨大的框架,官方居然沒有為用戶構造和區分這個概念,還要用戶自己去發現和總結,不得不說,是一大黑點。