函數(shù)式組件
function HelloMessage() {
let message = 'hello message'
return (
<div>
<div>{messgae}</div>
<button onClick={() => { message = 'hello react' }}>修改文本</button>
</div>
)
}
函數(shù)式組件的缺陷:
- 組件不會(huì)重新渲染:修改message之后叶洞,組件不知道自己要重新渲染
- 如果組件可以重新渲染,函數(shù)會(huì)被重新執(zhí)行,第二次執(zhí)行時(shí)message又會(huì)被初始化為"hello message"
- 沒(méi)有生命周期的回調(diào)
類組件
function HelloWorld (props) {
const imitateRequest = () => {
setTimeout(() => {
console.log('模擬函數(shù)式組件發(fā)送請(qǐng)求')
}, 1000)
}
imitateRequest()
return (
<div>
<h2>Hello World: {props.counter}</h2>
</div>
)
}
class App extends PureComponent {
constructor (props) {
super(props)
this.state = {
counter: 1
}
}
increment () {
this.setState({
counter: this.state.counter + 1
})
}
render() {
let { counter } = this.state
return (
<div>
<h1>App:{counter}</h1>
<button onClick={this.increment}>修改counter</button>
<HelloWorld counter={counter}/>
</div>
)
}
}
class組件相對(duì)于函數(shù)式組件有什么優(yōu)勢(shì)?比較常見(jiàn)的是下面的優(yōu)勢(shì):
-
class可以定義自己的state要出,用來(lái)保存組件內(nèi)部的狀態(tài)
- 而函數(shù)式組件不可以,因?yàn)楹瘮?shù)每次調(diào)用农渊,都會(huì)產(chǎn)生新的臨時(shí)變量
-
class有自己的生命周期厨幻,我們可以在對(duì)應(yīng)的生命周期中編寫(xiě)自己的邏輯
- 比如在componentDidMount中發(fā)送網(wǎng)絡(luò)請(qǐng)求,這個(gè)生命周期只會(huì)執(zhí)行一次
- 函數(shù)式組件腿时,如果在函數(shù)中發(fā)送網(wǎng)絡(luò)請(qǐng)求况脆,每次重新渲染都會(huì)重新發(fā)送一次網(wǎng)絡(luò)請(qǐng)求
-
class組件在狀態(tài)發(fā)生變化時(shí),只會(huì)重新執(zhí)行render函數(shù)和componentDidupdate等
- 函數(shù)式組件在重新渲染時(shí)批糟,整個(gè)函數(shù)都會(huì)被執(zhí)行
class組件的缺點(diǎn):
復(fù)雜組件變得難以理解:
對(duì)于邏輯復(fù)雜的組件格了,代碼會(huì)比較混亂,比如componentDidMount中徽鼎,就可能包含大量的邏輯代碼盛末,比如網(wǎng)絡(luò)請(qǐng)求,事件監(jiān)聽(tīng)等否淤。如果想要抽取通用的邏輯悄但,比如高階組件(HOC),又增加代碼的復(fù)雜度石抡。
難以理解的class: 要學(xué)習(xí)class,還必須搞清楚this的指向
-
組件復(fù)用狀態(tài)很難:
- 通過(guò)高階組件復(fù)用狀態(tài)
- Provider和Consumer來(lái)共享狀態(tài)檐嚣,但是多次使用Consumer時(shí),代碼就會(huì)存在很多嵌套
- 這些代碼不管是編寫(xiě)和設(shè)計(jì)啰扛,都比較困難
Hooks的出現(xiàn)
hooks的出現(xiàn)解決函數(shù)式組件的問(wèn)題嚎京,可以讓我們?cè)诤瘮?shù)式組件中使用state以及react的其他特性。
const Home = () => {
const [name, setName] = useState('hello title')
const handleName = ({ target: {value}}) => {
setName(value)
}
useEffect(() => {
document.title = name
}, [name])
const [width, setWidth] = useState(window.innerWidth)
useEffect(() => {
let handleResize = () => setWidth(window.innerWidth)
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
return (
<div>
<input value={name} onChange={handleName}/>
<span>{width}</span>
</div>
)
}
Hooks的好處:
-
函數(shù)式組件結(jié)合hooks讓整個(gè)代碼變得非常簡(jiǎn)潔
- 利用useEffect隐解,把業(yè)務(wù)邏輯分開(kāi)鞍帝,把之前放在不同生命周期里的相同功能的代碼,聚合在一起煞茫,把不同功能但是要在同一生命周期的代碼帕涌,隔離開(kāi),提高了代碼的可讀性和可維護(hù)性续徽。
-
解決了和狀態(tài)相關(guān)的邏輯復(fù)用問(wèn)題:
- 原來(lái)代碼復(fù)用使用的高階組件和渲染屬性都會(huì)增加組件樹(shù)層級(jí)蚓曼,使用起來(lái)也不方便,自定義hooks就可以解決這些問(wèn)題炸宵,而且使用更直觀方便辟躏;
并且再也不用考慮this相關(guān)的問(wèn)題谷扣;
-
更好的體現(xiàn)了react的開(kāi)發(fā)思想土全,從state->view的函數(shù)式映射
- 可以把UI的展示看成一個(gè)函數(shù)執(zhí)行的過(guò)程捎琐,Model就相當(dāng)于輸入的參數(shù),UI就相當(dāng)于函數(shù)的執(zhí)行結(jié)果裹匙;react要保證的是每當(dāng)狀態(tài)發(fā)生變化時(shí)瑞凑,函數(shù)就會(huì)重新執(zhí)行,并且會(huì)生成新的dom樹(shù)概页,然后react會(huì)把新的dom樹(shù)籽御,以最優(yōu)的方式更新到瀏覽器上;
Hooks的注意事項(xiàng):
- 只能在函數(shù)最外層調(diào)用hook惰匙,不能在循環(huán)技掏、條件判斷或者子函數(shù)中調(diào)用
- 只能在React的函數(shù)組件或者自定義Hooks中調(diào)用Hook,不能在其他js函數(shù)中調(diào)用
- 保證狀態(tài)最小化项鬼,需要渲染到UI里的保存在state里哑梳,不需要渲染的可以保存在useRef里;或者一些可以通過(guò)其他狀態(tài)計(jì)算得出的數(shù)據(jù)绘盟;
- 不要用props去初始化state鸠真,可以在useEffect中通過(guò)監(jiān)聽(tīng)props的變化去設(shè)置state;
有了Hooks以后得函數(shù)式組件,依然存在一些問(wèn)題:
-
事件處理函數(shù)會(huì)被重復(fù)定義龄毡,數(shù)據(jù)計(jì)算過(guò)程沒(méi)有緩存
- 這些問(wèn)題就需要利用useCallback吠卷、useMemo、useRef這些Hook來(lái)做一些優(yōu)化
對(duì)一些類組件的生命周期函數(shù)還不支持沦零,還不能實(shí)現(xiàn)getSnapShotBeforeUpdate和componentDidCatch這個(gè)生命周期祭隔;