在初學 React 的時候,分不清 React 元件和 React 元素,著實踩了一些坑。搞清楚 React 中什麼是元件,什麼是元素,既可以理清楚概念,也可以讓你避免一些不必要的錯誤。
React 元素
React 元素(React element),它是 React 中最小基本單位,我們可以使用 JSX 語法輕鬆地建立一個 React 元素:
const element = <div className="element">I`m element</div>
React 元素不是真實的 DOM 元素,它僅僅是 js 的普通物件(plain objects),所以也沒辦法直接呼叫 DOM 原生的 API。上面的 JSX 轉譯後的物件大概是這樣的:
{
_context: Object,
_owner: null,
key: null,
props: {
className: `element`,
children: `I`m element`
},
ref: null,
type: "div"
}
只有在這個元素渲染被完成後,才能通過選擇器的方式獲取它對應的 DOM 元素。不過,按照 React 有限狀態機的設計思想,應該使用狀態和屬性來表述元件,要儘量避免 DOM 操作,即便要進行 DOM 操作,也應該使用 React 提供的介面ref
和getDOMNode()
。一般使用 React 提供的介面就足以應付需要 DOM 操作的場景了,因此像 jQuery 強大的選擇器在 React 中幾乎沒有用武之地了。
除了使用 JSX 語法,我們還可以使用 React.createElement()
和 React.cloneElement()
來構建 React 元素。
React.createElement()
JSX 語法就是用React.createElement()
來構建 React 元素的。它接受三個引數,第一個引數可以是一個標籤名。如div
、span
,或者 React 元件。第二個引數為傳入的屬性。第三個以及之後的引數,皆作為元件的子元件。
React.createElement(
type,
[props],
[...children]
)
React.cloneElement()
React.cloneElement()
與React.createElement()
相似,不同的是它傳入的第一個引數是一個 React 元素,而不是標籤名或元件。新新增的屬性會併入原有的屬性,傳入到返回的新元素中,而就的子元素獎盃替換。
React.cloneElement(
element,
[props],
[...children]
)
React 元件
React 中有三種構建元件的方式。React.createClass()
、ES6 class
和無狀態函式。
React.createClass()
React.createClass()
是三種方式中最早,相容性最好的方法。在0.14版本前官方指定的元件寫法。
var Greeting = React.createClass({
render: function() {
return <h1>Hello, {this.props.name}</h1>;
}
});
ES6 class
ES6 class
是目前官方推薦的使用方式,它使用了ES6標準語法來構建,但它的實現仍是呼叫React.createClass()
來實現了,ES6 class
的生命週期和自動繫結方式與React.createClass()
略有不同。
class Greeting extemds React.Component{
render: function() {
return <h1>Hello, {this.props.name}</h1>;
}
};
無狀態函式
無狀態函式是使用函式構建的無狀態元件,無狀態元件傳入props
和context
兩個引數,它沒有state
,除了render()
,沒有其它生命週期方法。
function Greeting (props) {
return <h1>Hello, {props.name}</h1>;
}
React.createClass()
和ES6 class
構建的元件的資料結構是類,無狀態元件資料結構是函式,它們在 React 被視為是一樣的。
元素與元件的區別
元件是由元素構成的。元素資料結構是普通物件,而元件資料結構是類或純函式。除此之外,還有幾點區別要注意:
this.props.children
在 JSX 中,被元素巢狀的元素會以屬性 children 的方式傳入該元素的元件。當僅巢狀一個元素時,children 是一個 React 元素,當巢狀多個元素時,children 是一個 React 元素的陣列。可以直接把 children 寫入 JSX 的中,但如果要給它們傳入新屬性,就要用到React.cloneElement()
來構建新的元素。我曾放過以下錯誤:
render () {
var Child = this.props.children
return <div><Child tip={`error!`}/><div>
}
因為 Child 是一個 React 元素,而不是元件,這樣的寫法是完全錯誤的,正確的方式應該是:
render () {
var child = this.props.children
return <div>{ React.cloneElement(child, {tip: `right way!`}) }<div>
}
就這樣,原有屬性和新新增的屬性被一併傳入了子元素。使用React.cloneElement()
才是操作元素的正確姿勢。
使用者元件
有的時候,元件可以讓使用者以屬性的方式傳入自定義的元件,來提升元件的靈活性。這個屬性傳入的就應該是 React 元素,而非 React 元件。使用 React 元素可以讓使用者傳入自定義元件的同時,為元件新增屬性。同樣,可以使用React.cloneElement()
為自定義元件新增更多屬性,或替換子元素。
// 推薦
<MyComponent tick={
<UserComponent tip="Yes"/>
} />
// 不推薦
<MyComponent tick={ UserComponent } />
最後
最後,打個不恰當的比喻,React 元件是MyComponent
,React 元素就是<MyComponent />
。