1唉匾、setState(newState,callback)
React中可以通過修改state孕讳,通知React進(jìn)行Update的方法
callback用途,接收更新后的state,可以繼續(xù)操作
一般情況下 是一個(gè)異步方法
但是在定時(shí)器以及原生DOM事件會(huì)是一個(gè)同步方法
異步方法的好處
setState設(shè)計(jì)為異步厂财,可以顯著的提升性能油啤;
如果每次調(diào)用setState都進(jìn)行一次更新,那么意味著render函數(shù)會(huì)被頻繁調(diào)用蟀苛,界面重新渲染益咬,這樣效率是很低的;
最好的辦法應(yīng)該是獲取到多個(gè)更新帜平,之后進(jìn)行批量更新幽告;
如果同步更新了state,但是還沒有執(zhí)行render函數(shù)裆甩,那么state和props不能保持同步冗锁;
state和props不能保持一致性,會(huì)在開發(fā)中產(chǎn)生很多的問題嗤栓;
在調(diào)用setState后直接使用新的state是不建議的
this.setState({id:'123'});
console.log(this.state.id) // 這時(shí)候id不一定是 123
應(yīng)該寫成
this.setState({id:'123'},(state)=>{
console.log(state.id) // 這時(shí)候 id===123
})
異步變同步(不推薦)
class MyComponent extend React.Component{
constructor(){
this.state={
id:'haha'
}
}
componentDidMount(){
document.getElelemtnById('btn').addEventListener('clcik',()=>{
this.setState({id:'123'});
console.log(this.state.id) // 這時(shí)候id是 123
})
}
updateWithsetTimeout = ()=>{
setTimeOut(()=>{
this.setState({id:'123'});
console.log(this.state.id) // 這時(shí)候id是 123
},0)
}
render(){
return (
<>
<button onClick={this.updateWithsetTimeout}>setTimeOut</button>
<button id="btn">js function </button>
</>
)
}
}
2冻河、受控組件
在React 中,可變狀態(tài)(mutable state)通常保存在組件的state 屬性中茉帅,并且只能通過使用setState()來更新叨叙。
我們將兩者結(jié)合起來,使React的state成為“唯一數(shù)據(jù)源”堪澎;
渲染表單的React 組件還控制著用戶輸入過程中表單發(fā)生的操作擂错;
被React 以這種方式控制取值的表單輸入元素就叫做“受控組件”;
由于在表單元素上設(shè)置了value屬性樱蛤,因此顯示的值將始終為this.state.value钮呀,這使得React 的state 成為唯一數(shù)據(jù)源。
由于handleUsernameChange在每次按鍵時(shí)都會(huì)執(zhí)行并更新React 的state昨凡,因此顯示的值將隨著用戶輸入而更新爽醋。
class FormComponent extends React.Component{
constructor(){
this.state = {
inputValue:'',
}
}
onChange = (e) =>{
const value = e.target.value;
this.setState({inputValue:value});
}
render(){
return (
<input type="text" value={this.state.inputValue} onChange={this.onChange} />
)
}
}
3、非受控組件
如果要使用非受控組件中的數(shù)據(jù)便脊,那么我們需要使用ref來從DOM節(jié)點(diǎn)中獲取表單數(shù)據(jù)
在非受控組件中通常使用defaultValue來設(shè)置默認(rèn)值
class FormComponent extends React.Component{
constructor(){
this.inputRef = React.createRef();
}
onBtnClick = () =>{
console.log(
`input - ${this.inputRef.current.value}`
);
}
render(){
return (
<>
<input defaultValue="haha" ref={this.inputRef} />
<button onClick={this.onBtnClick}>確定</button>
</>
)
}
}
4蚂四、高階組件
至少滿足以下條件之一:
接受一個(gè)或多個(gè)函數(shù)作為輸入;
輸出一個(gè)函數(shù)就轧;
高階組件的英文是Higher-Order Components证杭,簡(jiǎn)稱為HOC田度;
官方的定義:高階組件是參數(shù)為組件妒御,返回值為新組件的函數(shù);
我們可以進(jìn)行如下的解析:
首先镇饺,高階組件本身不是一個(gè)組件乎莉,而是一個(gè)函數(shù);
其次,這個(gè)函數(shù)的參數(shù)是一個(gè)組件惋啃,返回值也是一個(gè)組件哼鬓;
function higherOrderComponent(component){
class Component extends React.Component{
render (){
{/* 需要將接收的props進(jìn)行向下傳遞 */}
return <component {...this.props}/>
}
}
Component.displayName = "EnhancedComponent";
return Component;
}
const EnhancedComponent = higherOrderComponent(WarppedComponent);
應(yīng)用場(chǎng)景一:props 增強(qiáng)
1、不改變?cè)写a情況下边灭,擴(kuò)增props react-redux的connect就是這個(gè)作用
2异希、利用高階組件進(jìn)行Context共享(減少業(yè)務(wù)組件在使用時(shí)對(duì)Context.Consumer 編寫不友好的問題)
應(yīng)用場(chǎng)景二:渲染判斷鑒權(quán)
加一層判斷,控制組件是否渲染
應(yīng)用場(chǎng)景三:生命周期劫持
虛空增加的一層父組件绒瘦,可以在原需要組件進(jìn)行渲染的時(shí)候称簿,根具特定的生命周期進(jìn)行一些類似于 熱點(diǎn)、渲染時(shí)間惰帽、錯(cuò)誤邊界捕獲等記錄憨降。
HOC的意義
我們會(huì)發(fā)現(xiàn)利用高階組件可以針對(duì)某些React代碼進(jìn)行更加優(yōu)雅的處理。
其實(shí)早期的React有提供組件之間的一種復(fù)用方式是mixin该酗,目前已經(jīng)不再建議使用:
Mixin可能會(huì)相互依賴授药,相互耦合,不利于代碼維護(hù)
不同的Mixin中的方法可能會(huì)相互沖突pMixin非常多時(shí)呜魄,組件是可以感知到的悔叽,甚至還要為其做相關(guān)處理,這樣會(huì)給代碼造成滾雪球式的復(fù)雜性
當(dāng)然爵嗅,HOC也有自己的一些缺陷:
HOC需要在原組件上進(jìn)行包裹或者嵌套骄蝇,如果大量使用HOC,將會(huì)產(chǎn)生非常多的嵌套操骡,這讓調(diào)試變得非常困難九火;
HOC可以劫持props,在不遵守約定的情況下也可能造成沖突册招;
Hooks的出現(xiàn)岔激,是開創(chuàng)性的,它解決了很多React之前的存在的問題
比如this指向問題是掰、比如hoc的嵌套復(fù)雜度問題等等虑鼎;
5、Portals的使用
通常來講键痛,當(dāng)你從組件的render方法返回一個(gè)元素時(shí)炫彩,該元素將被掛載到DOM節(jié)點(diǎn)中離其最近的父節(jié)點(diǎn):
Portals提供了一種將子節(jié)點(diǎn)渲染到存在于父組件以外的 DOM 節(jié)點(diǎn)的方案
ReactDOM.createPortal(child, container)
第一個(gè)參數(shù)(child)是任何可渲染的React 子元素,例如一個(gè)元素絮短,字符串或fragment江兢;
第二個(gè)參數(shù)(container)是一個(gè)DOM元素;
即使child子元素被掛載到其他DOM結(jié)點(diǎn)下丁频,只是DOM級(jí)掛載杉允,但是在Fiber樹上邑贴,它仍屬于原Parent結(jié)點(diǎn)的子節(jié)點(diǎn)。
現(xiàn)象級(jí)掛載
就是從HTML代碼結(jié)構(gòu)上可以看到是確實(shí)被挪移到目標(biāo)結(jié)點(diǎn)下
目標(biāo)結(jié)點(diǎn)通過添加事件監(jiān)聽叔磷,也能捕獲child子元素對(duì)應(yīng)冒泡的事件
仍屬于Parent原結(jié)點(diǎn)
1拢驾、仍然可以使用Context,不脫離作用域范圍。
2改基、在父結(jié)點(diǎn)上添加的React事件(onClick)繁疤,在子節(jié)點(diǎn)上發(fā)生時(shí)事件時(shí),仍能被冒泡捕獲到秕狰。
3嵌洼、但是通過elemnt.addEventListener 這種方式就不能再捕獲對(duì)應(yīng)事件!因?yàn)镈OM元素真的被移走了封恰!
<Parent onClick>
<Portal>
<Button />
</Portal>
</Parent>
即使這里的Portal被移動(dòng)到其他DOM結(jié)點(diǎn)下麻养,當(dāng)點(diǎn)擊Button時(shí),仍能被Parent的onClick事件所捕獲