組件是 React 里非常重要的組成部分赁项,其分為函數(shù)組件和 Class 組件搏色。本文就簡(jiǎn)單說(shuō)明這兩種組件定義方式的由來(lái)涩僻。
例子
讓我們先從一個(gè)簡(jiǎn)單的需求開(kāi)始晌涕。定義一個(gè)加減器饺蔑,就是用來(lái)做簡(jiǎn)單的加減法锌介。使用 JSX 語(yǔ)法我們可以寫(xiě)成這樣:
let number = 0
let add = () => {
number += 1
render()
}
let minus = () => {
number -= 1
render()
}
let render = () => {
ReactDOM.render(
<div className="parent"> // Adder1
<span className="red">{number}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
</div>,
document.querySelector('#root'))
}
render()
函數(shù)組件
現(xiàn)在我們想要更多的加減器,那么可能會(huì)在 render()
函數(shù)里寫(xiě)很多個(gè) <div className="parent">...</div>
猾警,這樣明顯不好孔祸。
還記得 JSX 語(yǔ)法里的并不是真正的 HTML,而是虛擬 DOM发皿,即 JS 代碼崔慧,不信可看轉(zhuǎn)譯后的結(jié)果:
既然是 JS 代碼,那么我們就可以用 JS 的方法來(lái)將其分塊了穴墅。我們定義多個(gè)函數(shù)惶室,來(lái)返回虛擬 DOM 不就可以完成分塊了么?所以定義兩個(gè)函數(shù):
function App() {
return (
<div>
<Adder1/> // React.createElement(Adder1)
<Adder2/> // React.createElement(Adder2)
</div>
)
}
function Adder1() {
return (
<div className="parent">
<span className="red">{number}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
</div>
)
}
...
雖然這里有個(gè)問(wèn)題玄货,變量 number
被 Adder1 和 Adder2 共享了皇钞。但是 React 的一個(gè)簡(jiǎn)單組件就誕生了,其本質(zhì)就是一個(gè)函數(shù)松捉。
props
React 的開(kāi)發(fā)者很聰明夹界,即然這個(gè)組件返回的是虛擬 DOM,那么正常的 DOM 應(yīng)該要有屬性才行隘世,而函數(shù)的參數(shù)好像和這屬性有著某種相關(guān)性可柿。所以函數(shù)組件的一個(gè)特性被開(kāi)發(fā)出來(lái)了:函數(shù)組件傳入的參數(shù)(對(duì)象) 代表了該虛擬 DOM 的屬性。例子:
function Adder (props) {
return (
<div className="parent">
<p>My name is {props.name}, age: {props.age}</p>
</div>
)
}
function App() {
return (
<div>
<Adder name="Adder 1" age="12"/>
</div>
)
}
state
那么自身的屬性呢丙者?很簡(jiǎn)單复斥,在函數(shù)里面定義就好了:
function Adder1(props) {
let name = 'hello'
return (
<div className="parent">
<p>My name is {name}, age: {props.age}</p>
</div>
)
}
Class 組件
好了,現(xiàn)在說(shuō)說(shuō)怎么去解決共享 number
的問(wèn)題械媒。像上面說(shuō)的用自身屬性試試:
function Adder1(obj) {
let number = 0
function add() {...}
function minus() {...}
return (
<div className="parent">
<span className="red">{number}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
</div>
)
}
...
雖然好像看起來(lái)沒(méi)問(wèn)題目锭,但是每次修改值后我們都要重新 render 一下組件的,render 的時(shí)候需要執(zhí)行 Adder1
里面的代碼,number
被重置了痢虹。
React 的 Class 組件就了為了這樣的問(wèn)題而誕生的:
class Adder1 extends React.Component {
constructor(props) {
super(props)
this.state = {
number: 0
}
}
add() {
this.setState({
number: this.state.number + 1
})
}
minus() {
this.setState({
number: this.state.number - 1
})
}
render() {
return (
<div className="red">
<span>{this.state.number}</span>
<button onClick={this.add.bind(this)}>+1</button>
<button onClick={this.minus.bind(this)}>-1</button>
{this.props.name}
</div>
)
}
}
注意
- 必須要繼承
React.Component
-
constructor
里要傳入props
键俱,并調(diào)用super(props)
,因?yàn)檫@是 ES6 語(yǔ)法的規(guī)定 -
this.state
用來(lái)存放自身屬性世分,但是修改的時(shí)候要用this.setState(newState)
编振,而不能直接this.state.number += 1
- 綁定方法的時(shí)候要綁定
this
,因?yàn)?React 會(huì)這樣調(diào)用this.add.call(undefined, ...)
setState
為了對(duì)更新進(jìn)行優(yōu)化臭埋,如果多次修改 this.state
會(huì)將大批量更新合并成一次更新踪央。其實(shí) this.state
的更新方法屬于異步更新,只有全部改變完了再去更新瓢阴。
像下面的代碼只會(huì)更新一次:
this.setState({
number: this.state.number - 1
})
this.setState({
number: this.state.number - 1
}) // 只是一次 - 1畅蹂,因?yàn)槊慷蔚臅r(shí)候 this.state.number 還可能是 0
當(dāng)然可以用回調(diào)的形式進(jìn)行多次更新:
this.setState((state) => {
return { number: state.number - 1 }
})
this.setState((state) => {
return { number: state.number - 1 }
})