介紹倆日常沒使用的React API,近日踩雷了,遂藉此篇提出來品品...javascript
首先這倆貨同屬於React的頂層API,即咱們import React from 'react';
後,能夠經過React.xxx
的方式來調用。html
再看官方文檔對它們的劃分:java
圖中的幾個API都是對React
元素進行操做的,isValidElement
就不贅述了,用來校驗入參是不是一個合法的React
元素,返回一個布爾值。node
咱們都知道在props
對象中還有children
這個屬性。它可以從某種程度上減小咱們在一個組件內的嵌套層級,可能這樣描述有點抽象,舉個栗子:react
// 好比咱們有個Modal模態框組件
export default class Modal extends React.Component {
//...
}
// 有不少場景須要在Modal框內展現子組件的東西,最多見的結構相似下面
<Modal>
<Content /> </Modal>
// 的確能夠在定義Modal的文件內import子組件,但咱們這是一個公共的組件,它僅是一個套套,因此一般會使用下面這種方案
render() {
return (
<div> {this.props.children} </div>
)
}
複製代碼
這樣來講,咱們的父組件就和可能傳入的children
解耦了,各個模塊都是獨立的,各司其職。更多的關於props.children
的語法闡釋能夠閱讀官方文檔。數組
看到這裏,咱們也發現了一個問題,就是props.children
對於咱們開發者來講就是一個黑盒,咱們對它可能傳入的數據結構是不可知的(表達式、布爾、render function
等等),若是咱們沒有對其進行操做,那其實沒什麼所謂。但只要咱們對其進行操做了,好比下意識覺得是個數組進行props.children.map
這樣的調用就要注意,非Array
就直接報TypeError
了。那怎麼處理相似這樣的情景呢?數據結構
其實React.Children
剛好就是爲咱們提供處理props.children
數據結構能力的API。注意這裏React.Children
的Children
是大寫。dom
React.Children.map(children, function[(thisArg)])
這個類方法可以cover前文我提到的未知數據結構下的遍歷問題,只須要簡單修改:函數
React.Children.map(props.children, child => {})
複製代碼
能夠看到這個API接收兩個參數,第一個就是咱們一般要處理的黑盒prop.children
,第二個入參回調,其實就是咱們遍歷的元素上下文,經過它,咱們可以進行定製化的操做。this
筆者結合源碼獲得當props.children
爲null
和undefined
時,最終會原值返回,其他情景則是返回一個數組。
跟React.Children.map
相似,都是迭代操做,只不過這個不會返回數組。undefined
和null
時的判斷邏輯同上。
返回其中內部元素數,其值與前面兩個迭代方法的回調觸發次數相等。
用於判斷傳入的children
是否只有一個child
。注意接收類型是React element
。不能拿React.Children.map()
返回的結果再去判斷是幾個child
,由於此時你拿到的已然是一個Array
類型。
這個API會將黑盒的props.children
數據結構以扁平的Array
結構暴露給咱們,以下面這樣:
經常使用在往下傳props
時,從新排序或過濾部分children
的情景。
有了上面的鋪墊,這個API的引入就比較天然了,前文中咱們經過React.Children
的類方法獲得了訪問本是黑盒的props.children
的能力。React.cloneElement
則是能讓咱們在操做React element
時,進行淺層的新props merge
,傳入的新children
則會替換舊的children
。原element
的key
和ref
都會保留。
看下API定義:
React.cloneElement(
element,
[props],
[...children]
)
複製代碼
其實跟React.createElement
的構造有點像:
React.createElement(
type,
[props],
[...children]
)
複製代碼
畢竟是拷貝返回一個新的組合元素,React.cloneElement
處理element
時能夠大體理解成<element.type {...element.props} {...props}>{children}</element.type>
。
那這個API到底有啥用呢?舉一個場景:
<Tabs active=''>
<Tab id='a' title='a'>
Content: {Math.random()}
</Tab>
<Tab id='b' title='b'>
Content: {Math.random()}
</Tab>
<Tab id='c' title='c'>
Content: {Math.random()}
</Tab>
</Tabs>
複製代碼
我但願點擊對應Tab
的時候,再顯示Content信息,而且再也不修改以上組件結構(不額外在每一個子組件上加onClick
的props
),實際展現相似下圖:
此時,咱們已經瞭解了前文中介紹的API的能力,大體有兩種解決方案,主體思路是一致的,區分在是否是每一個子組件都掛一個回調亦或在父組件上掛一個事件代理,去判斷。
這裏我使用HOOKS的函數式寫法:
// 事件代理
const Tabs = props => {
const { children, ...rest } = props;
const [active, setActive] = useState(rest.active);
let handleClick = e => {
if (e.target.nodeName === 'A') {
setActive(e.target.id);
}
}
return (
<header> <nav className={styles.nav}> <ul onClick={handleClick}> { React.Children.map(children, child => React.cloneElement(child, {active: active})) } </ul> </nav> </header>
)
}
複製代碼
// 每個child 都綁定回調 經過cloneElement傳props
const Tabs = props => {
const { children, ...rest } = props;
const [active, setActive] = useState(rest.active);
let toggleActive = (e, id) => {
e.preventDefault();
setActive(id);
}
return (
<header> <nav className={styles.nav}> <ul> { React.Children.map(children, child => React.cloneElement(child, {active: active, toggleActive: toggleActive})) } </ul> </nav> </header>
)
}
複製代碼
主體思想都相似,就是把子組件須要的屬性和回調函數經過cloneElement
的方式merge
進去。
以上DEMO,可藉此傳送門移步。
React.Children
提供了咱們直接訪問黑盒props.children
數據結構的能力;
React.cloneElement
接收一個React element
並支持往其中淺層合併props
,替換舊children
;筆者看來該API能夠從必定程度上減小代碼的重複書寫,使組件標籤表達更加清晰。