組件(Component)
1、組件:能跟其他物件組合起來(lái)的物件
2、就目前而言,一個(gè)返回React元素的“函數(shù)”就是組件
3、在Vue里,一個(gè)構(gòu)造選項(xiàng)就可以表示一個(gè)組件
元素與組件
元素:const div = React.createElement('div',...)
//這是一個(gè)React元素(d是小寫(xiě)的)
組件:const Div = ()=>React.createElement('div'..)
//這是一個(gè)React組件(D是大寫(xiě)的)
React 的兩種組件
1、類組件
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0
};
}
add() {
this.setState({ n: this.state.n + 1 });
}
render() {
return (
<div className="Son">
兒子n :{this.state.n}
<button onClick={() => this.add()}>+1</button>
<Grandson />
</div>
);
}
}
類組件注意事項(xiàng):
- this.state.n += 1 無(wú)效?
//其實(shí)n已經(jīng)變了,UI沒(méi)自動(dòng)更新,
//調(diào)用 setState 才會(huì)觸發(fā)更新(異步更新,這里復(fù)制原有n,得到一個(gè)新的n)
//React不不像Vue一直監(jiān)聽(tīng)data 所以不會(huì)更新 - setState 會(huì)異步更新UI
//setState 之后,state不會(huì)立馬改變,立馬讀會(huì)失敗
//推薦使用setState(函數(shù))
2、函數(shù)組件
const Grandson = () => {
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
孫子n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
函數(shù)組件注意事項(xiàng):
沒(méi)有 this,一律使用參數(shù)和變量
組件使用方法:
<Welcome />會(huì)被翻譯成什么<div />會(huì)被翻譯為React.createElement('div')<Welcome />翻譯為React.createElement(Welcome)可以用babel online直接翻譯給你看
React.createElement的邏輯
如果傳入一個(gè)字符串div, 則會(huì)創(chuàng)建div
如果傳入一個(gè)函數(shù),則會(huì)調(diào)用該函數(shù),獲取其返回值
如果傳入一個(gè)類,則在類前面加個(gè)new(這會(huì)導(dǎo)致執(zhí)行constructor), 獲取一個(gè)組件對(duì)象,然后調(diào)用對(duì)象的render方法,獲取其返回值
props - 外部數(shù)據(jù)
const root =document.getElementById('root')
const React = window.React
const ReactDOM = window.ReactDOM
// import React from 'react'
// import ReactDOM from 'react-dom'
function App(){
return(
<div className="App">
爸爸
<Son x="吃飯"/>
</div>
)
}
class Son extends React.Component{
constructor(){
super()
this.state = {
n:0
}
}
add(){
this.setState({n:this.state.n+1})
}
render(){
return(
<div>
兒子 n:{this.state.n},{this.props.x} //---------類組件直接用this.props接收值
<button onClick={()=>this.add()}> +1 </button>
<Grandson y="睡覺(jué)"/>
</div>
)
}
}
const Grandson =(props) =>{
const [n,setN] = React.useState(0)
return(
<div>
孫子 n:{n},{props.x}//---------函數(shù)組件接收一個(gè)props的參數(shù)不需要this就可以接收值
<button onClick={()=>setN(n+1)}> +1 </button>
</div>
)
}
ReactDOM.render(App(),root);
state - 內(nèi)部數(shù)據(jù)
- 鏈接
- 類組件用this.state讀,this.setState寫(xiě)
- 函數(shù)組件用useState返回?cái)?shù)組,第一項(xiàng)讀,第二項(xiàng)寫(xiě)
- 關(guān)注數(shù)據(jù)的時(shí)候我們需要關(guān)注,初始化,怎么讀,怎么寫(xiě)
- 直接改state的n,ui不會(huì)刷新,vue里面是對(duì)data做了劫持,必須要用setState,
- 另外不推薦對(duì)象局部改變了,又原封不動(dòng)的給setState,效果是ok的但是一般推薦要產(chǎn)生一個(gè)新的對(duì)象
- 這就叫做數(shù)據(jù)不可變,以前的數(shù)據(jù)不要改,要改就產(chǎn)生一個(gè)新的對(duì)象
this.setState({ n: this.state.n + 1 }); - 牛x的前端用setState(函數(shù)),這樣寫(xiě)的好處,打印n的值發(fā)現(xiàn)不對(duì),因?yàn)閟etState是異步的更新ui的過(guò)程,好處就是避免異步造成的誤解
this.setState((state)=>{
return {n: state.n+1}
})
// 如果用這種寫(xiě)法就很清楚的知道舊的state和新的state
// 打印n
this.setState(state=>{
const n = state.n + 1
console.log(n)
return {n}
})
setState 注意事項(xiàng)
- this.state.n += 1無(wú)效?
- 其實(shí)n已經(jīng)改變了,只不過(guò)UI不會(huì)自動(dòng)更新而已
- 調(diào)用setState才會(huì)觸發(fā)UI更新(異步更新)
- 因?yàn)镽eact沒(méi)有像Vue監(jiān)聽(tīng)data一樣監(jiān)聽(tīng)state
- setState會(huì)異步更新UI
- setState之后,state不會(huì)馬上改變,立馬讀state會(huì)失敗
- 更推薦的方式是setState(函數(shù)),函數(shù)接受一個(gè)舊的state返回一個(gè)新的state
- this.setState(this.state)不推薦
- React希望我們不要修改舊state(不可變數(shù)據(jù))
- 常用代碼: setState({n: state.n + 1})
- 總結(jié),這是一種理念(函數(shù)式)
函數(shù)組件注意事項(xiàng)
- 跟類組件類似的地方,也要通過(guò)setX(新值)來(lái)更新UI
- 跟類組件不同的地方,沒(méi)有this,一律用參數(shù)和變量
復(fù)雜的state
如果state里不止有n怎么辦
(類組件里有n和m)[https://codesandbox.io/s/friendly-aryabhata-eq24y]
(函數(shù)組件里有n和m)[https://codesandbox.io/s/funny-montalcini-wszxe]
函數(shù)組件另一種(不推薦的寫(xiě)法)[https://codesandbox.io/s/distracted-taussig-dmkjz],寫(xiě)完會(huì)發(fā)現(xiàn)m被置空
總結(jié)復(fù)雜的state
- 類組件的setState會(huì)自動(dòng)合并第一層屬性
- (但是不會(huì)合并第二層屬性)[https://codesandbox.io/s/dreamy-pike-w9je2],需要使用(Object.assign)[https://codesandbox.io/s/zen-margulis-gjr7t],或者(...操作符)[https://codesandbox.io/s/dawn-sound-rwyxv]
- 函數(shù)組件的setX則完全不會(huì)合并state,需要使用Object.assign,或者(...操作符)
- ...在react是經(jīng)常使用的
事件綁定
類組件的事件綁定
<button onClick={() => this.addN()}>n + 1</button>
// 傳一個(gè)函數(shù)給onClick即可,注意C大寫(xiě)
// 思考一個(gè)問(wèn)題,下面這樣寫(xiě)行不行
<button onClick={this.addN}>n + 1</button>
// 有問(wèn)題,這樣使得this.addN里的this變成window
<button onClick={this.addN.bind(this)}>n+1</button>
// 這樣寫(xiě)是可以的,因?yàn)樗祷匾粋€(gè)綁定了當(dāng)前this的新函數(shù)
// 但是這樣寫(xiě)太麻煩,你不如第一種
// 但是第一種寫(xiě)法依然太長(zhǎng),可用this._addN = ()=> this.addN()
// 給箭頭函數(shù)取個(gè)名字,然后寫(xiě)成,在構(gòu)造函數(shù)中賦值
<button onClick={this._addN}>n+1</button>
// 給箭頭函數(shù)取個(gè)名字,然后寫(xiě)成
<button onClick={this._addN}>n + 1</button>
// 這樣又不如寫(xiě)成
constructor() {
this.addN = ()=> this.setState({n: this.state.n + 1})
}
render(){
return <button onClick={this.addN}>n + 1</button>
}
但這樣寫(xiě)不如聲明addN結(jié)構(gòu)清晰
最終的方案,類組件的事件綁定最好的方法
class Son extends React.Component {
addN = () => this.setState({n: this.state.n + 1});
render() {
return <button onClick={this.addN}>n+1</button>
}
}