組件化開發的時候,參數傳遞是很是關鍵的環節html
哪些參數放在組件內部管理,哪些參數由父組件傳入,哪些狀態須要反饋給父組件,都須要在設計組件的時候想清楚react
但實現這些交互的基礎,是明白組件之間參數傳遞的方式,和各自的優缺點npm
1、父組件傳參到子組件redux
和 Vue 同樣,React 中從父組件到子組件的傳參也是經過 props數組
不過在 Vue 項目中,須要在先組件裏定義將要接收的 props,而 React 能夠直接獲取less
並且 props 不光能夠接收 Number、String 等基本類型,還能夠接收 Function,這點在《從 Vue 的視角學 React(三)—— 事件處理》中也有提到ide
另外 props 還能夠直接接收組件,相似於 Vue 中的 slot,後面會詳細介紹函數
2、子組件向父組件傳參工具
在項目中,也會有須要更新 props 的時候組件化
但和 Vue 同樣,React 中的 props 也是單向的,必定不能在組件內部直接修改 props 的值
而應該採用事件上報的方式,讓父組件修改相應的狀態
整個過程就像是 Vue 中的 $emit,只是將 $emit 中聲明的自定義事件放到了 props 中
上面的 updateText 就是一個自定義事件,父組件爲這個自定義事件添加了處理函數 handleUpdateText
若是子組件須要向父組件傳遞參數,能夠在觸發自定義事件 updateText 的時候,向事件處理函數傳參
而後父組件在對應的事件處理函數 handleUpdateText 中獲取到子組件傳過來的參數
3、狀態提高
除了父子之間的傳參,還會有兩個平級組件之間的參數傳遞
React 的推薦方案是,將兩個子組件的參數都放到父組件中處理,這就是狀態提高
假設有 boy 和 girl 兩個組件,它們都是組件 father 的子組件
組件 girl 中有一個 food 狀態,組件 boy 中有一個 age 狀態,food 會隨着 age 的變化而變化
若是 age 和 food 都是對應組件的 state,維護起來就比較麻煩
而若是將 age 和 food 都做爲 props 傳入,就能夠在父組件 father 中維護這兩個狀態
當須要更新 age 的時候,從 boy 組件上報事件,而後在父組件中同時更新 food 和 age
React 是單向數據流,在設計組件的時候,應當始終保持自上而下的數據傳遞,而不是嘗試在不一樣組件間同步 state
若是某些數據能夠由 props 或 state 推導得出,那麼它就不該該存在於 state 中
雖然這種方式會比雙向綁定須要編寫更多的代碼,但更利於維護
4、限制 props 類型
良好的組件不可避免的會用到不少 props,爲這些 props 添加類型校驗就尤其重要
Vue 自己就有一套很完善的 props 類型校驗配置,React 以前也有 React.PropTypes
但支持 TypeScript 以後,就將這部分功能拆成了獨立的第三方庫 prop-types
npm install prop-types -S
引入 PropTypes 以後,在組件內定義一個靜態屬性 propTypes(注意大小寫),而後定義具體的規則
參考上圖的示例,校驗規則由 . 語法鏈接,若是有多個類型,就使用 oneOfType
更多關於 PropTypes 的使用能夠查看官方文檔
除了類型校驗,有時候還須要給 props 添加默認值,這時候能夠用 defaultProps
5、組件嵌套
雖然開發組件的時候,咱們老是但願能儘可能知足需求,以減小後期的工做量
但一萬個讀者就有一萬個哈姆雷特,一個組件不可能知足全部用戶的需求,因此有時候就須要爲組件提供一些擴展性
另外,還有一些組件自己就是做爲容器開發的,這些場景都須要將組件做爲 props 傳給子組件
const Child = props => <div>{props.content}</div>
const Tag = props => <div>This is tag</div>
const Parents = props => <Child content={<Tag />}/>
上面的代碼中,子組件 Child 會接收一個 props 屬性 content
而後在父組件 Parents 中,將 Tag 組件做爲 content 的值傳給了子組件 Child
不過這樣的寫法並不優雅,若是能像 Vue 的 slot 那樣,直接將子組件嵌套在父組件的標籤內,就更符合 HTML 標籤的結構
這時候就能夠用到 props.children
const Child = props => <div>{props.children}</div>
const Tag = props => <div>This is tag</div>
const Parents = props => <Child><Tag /></Child>
從上面的代碼能夠看出,組件標籤之間嵌套的內容,能夠在組件內經過 props.children 接收到
事實上,props.children 是一個數組,若是不加具體的元素下標,就會將全部的元素渲染出來
若是標籤內有多個節點,porps.children 就會將自身組件做爲根節點,以數組的形式將組件內的 DOM 結構虛擬出來
並且 props.children 不單能夠接收組件,還能夠接收字符串
6、Context
在維護大型項目的時候,僅靠 props 和事件來傳參是不夠的,因此 Vue 提供了 Vuex 來維護狀態
React 也有狀態管理工具,經常使用的有 Redux、Mobx,如何使用這些工具我會在之後的文章中介紹
但在使用這些工具以前,須要瞭解 context,由於 Redux 和 Mobx 都是基於 context 實現的
若是組件的層級很深,僅使用 props 層層嵌套傳參的話就很是的冗餘
這時若是有一個全局變量,在頂層組件定義以後,直接在底層組件中獲取,就會很是簡潔,context 就是這樣的全局變量
// Context 可讓咱們無須明確地傳遍每個組件,就能將值深刻傳遞進組件樹。 // 爲當前的 theme 建立一個 context(「light」爲默認值)。
const ThemeContext = React.createContext('light'); class App extends React.Component { render() { // 使用一個 Provider 來將當前的 theme 傳遞給如下的組件樹。
// 不管多深,任何組件都能讀取這個值。
// 在這個例子中,咱們將 「dark」 做爲當前的值傳遞下去。
return ( <ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
); } } // 中間的組件不再必指明往下傳遞 theme 了。
function Toolbar(props) { return ( <div>
<ThemedButton />
</div>
); } class ThemedButton extends React.Component { // 指定 contextType 讀取當前的 theme context。
// React 會往上找到最近的 theme Provider,而後使用它的值。
// 在這個例子中,當前的 theme 值爲 「dark」。
static contextType = ThemeContext; render() { return <Button theme={this.context} />;
} }
但 context 的使用會極大的加強組件之間的耦合性,項目中並不建議直接使用
因此我直接複製粘貼了官方文檔的代碼,僅爲了解 context 這個概念
小型項目中,若是有深層次的傳參,應當從組件設計上解決問題,好比直接將組件傳下去
而大型項目中,若是須要用到 context,更推薦使用 redux 和 mobx 這些成熟的狀態管理工具
參考資料: