Reac生命周期中組件創(chuàng)建階段的過程詳解
componentWillMount();
componentWillMount(): 組件將要掛載到頁面時觸發(fā)該函數(shù),此時組件還沒有掛載到頁面上嘴办。而且內(nèi)存中,虛擬的Dom結構還沒有被創(chuàng)建出來,但是初始化時的默認屬性和this.state的屬性是可以在函數(shù)中訪問到的,可以調用組件中定義的方法。類比Vue中生命周期的created()階段晦嵌。
注意點
ReactV16.3對生命周期的部分做了一些新的處理,為componentWillMount,componentWillReceiveProps搏熄,componentWillUpdate添加一個“UNSAFE_”前綴。 (這里暇赤,“不安全”不是指安全性心例,而是表示使用這些生命周期的代碼將更有可能在未來的React版本中存在缺陷,特別是一旦啟用了異步渲染)鞋囊。
所以在16.3及以后的版本中用UNSAFE_componentWillMount來替代componentWillMount止后,不替換也可以但是在控制臺會有警告。
詳細內(nèi)容:ReactV16.3即將更改的生命周期
render()
render():該階段是在內(nèi)存中創(chuàng)建虛擬Dom的階段溜腐,整個過程結束后译株,內(nèi)存中就已經(jīng)創(chuàng)建完成了一個虛擬Dom,但是并未將其掛載到頁面上挺益,所以在這個階段也是無法獲取任何dom元素的歉糜。
componentDidMount()
組件已經(jīng)掛載到了頁面上后就進入了該階段,在這個階段中望众,頁面已經(jīng)存在可見的Dom元素了匪补,在這個頁面可以隨心所欲的操作頁面上的dom元素,在該階段烂翰,在React中同樣也可以用原生js來為元素綁定事件夯缺,盡管react中可以用原生js來為元素綁定事件,但是react中最好不要直接去操作dom元素甘耿,我們通常使用react提供的機制綁定事件踊兜。之后組件就進入了運行階段。
React生命周期的創(chuàng)建階段的各個回調函數(shù)的演示
組件CounterThree.jsx
import React from 'react'
//導入?yún)?shù)傳遞校驗的模塊
import dataRules from 'prop-types'
export default class CounterThree extends React.Component{
constructor(props){
super(props)
this.state = {
message:'hello',
//將props.oneCount賦值給state中的一個屬性
count:props.oneCount
}
}
static defaultProps = {
oneCount:10
}
static propTypes = {
oneCount:dataRules.number
}
//componentWillMount 組件將要掛載到頁面時觸發(fā)該函數(shù),此時組件還沒有掛載到頁面上
//而且內(nèi)存中棵里,虛擬的Dom結構還沒有被創(chuàng)建出來润文,但是初始化時的默認屬性和this.state的屬性是可以在函數(shù)中訪問到的,可以調用組件中定義的方法
UNSAFE_componentWillMount(){
console.log(document.getElementById('oneDom'));//null 此時虛擬dom還未創(chuàng)建
console.log(this.props.oneCount)//100
console.log(this.state.message)//hello
this.oneMethod();//我是oneMethod方法
}
render(){
console.log(document.getElementById('oneDom'))
console.log('-------------------')
//在return之前 虛擬Dom還未創(chuàng)建完成殿怜,頁面是空的典蝌,拿不到任何元素
return <div>
<h3 id = "oneDom">這是一個Counter計數(shù)器組件</h3>
<button id="btn" onClick = {()=>this.changeData()}>點擊+1</button>
<hr/>
<h3>當前的數(shù)量是{this.state.count}</h3>
</div>
//return 執(zhí)行完畢之后,虛擬Dom結構已經(jīng)創(chuàng)建完头谜,但還沒有掛載到頁面上
}
componentDidMount(){
//該階段類比Vue中生命周期的mounted
//如果我們想操作dom元素骏掀,最早可以在該階段去操作
console.log(document.getElementById('oneDom'))
// document.getElementById('btn').onclick = ()=>{
// // console.log('原生js在react綁定的事件')
// this.setState({
// count:this.state.count+1
// })
// }
}
oneMethod(){
console.log('我是oneMethod方法')
}
changeData=()=>{
this.setState({
count:this.state.count+1
})
}
}
入口文件index.js
// console.log("React真好用")
import React from 'react'
import ReactDom from 'react-dom'
import CounterThree from '@/components/CounterThree'
ReactDom.render(<div>
{/* 在這里規(guī)定每個使用該組件的用戶必須傳遞一個默認的數(shù)量值,作為組件初始化的數(shù)據(jù) */}
<CounterThree oneCount = {100}></CounterThree>
</div>,document.getElementById('app'))
Reac生命周期中組件運行階段的過程詳解
shouldComponentUpdate(nextProps,nextState);
shouldComponentUpdate()判斷組件是否需要更新 返回布爾值;
返回true則會調用render()重新渲染頁面,之后數(shù)據(jù)和頁面都是最新的;
如果返回false,不會執(zhí)行后續(xù)的生命周期函數(shù)截驮,render()函數(shù)也不會調用笑陈,將會繼續(xù)返回組件的運行中的狀態(tài),數(shù)據(jù)得到更新葵袭,組件的state狀態(tài)會被修改涵妥,但是頁面并沒有重新渲染,是舊的坡锡。
在該組件中蓬网,通過this.state.count拿到的屬性值是舊的,并不是最新的鹉勒,在這里可以通過nextProps和nextState兩個參數(shù)去獲取到對應的最新的屬性值帆锋。
componentWillUpdate()
組件將要更新階段的狀態(tài),在該狀態(tài)下禽额,內(nèi)存中的虛擬dom和頁面上的dom還都是舊的锯厢,所以在該階段要謹慎操作dom,因為很可能只是操作的舊的Dom脯倒。
render()
運行階段再一次執(zhí)行render()函數(shù)实辑。在組件運行階段,componentWillUpdate()過后還會再次調用render()函數(shù)盔憨,在render()執(zhí)行完畢之前徙菠,頁面上的dom還是舊的。
componentDidUpdate()
組件完成了更新的狀態(tài)郁岩,在該狀態(tài)下婿奔,數(shù)據(jù)和內(nèi)存中的虛擬dom以及頁面上的dom都是最新的,此時可以放心大膽的去操作Dom问慎。
組件CounterFour.jsx
import React from 'react'
//導入?yún)?shù)傳遞校驗的模塊
import dataRules from 'prop-types'
export default class CounterThree extends React.Component{
constructor(props){
super(props)
this.state = {
message:'hello',
count:props.oneCount
}
}
static defaultProps = {
oneCount:10
}
static propTypes = {
oneCount:dataRules.number
}
UNSAFE_componentWillMount(){
console.log(document.getElementById('oneDom'));//null 此時虛擬dom還未創(chuàng)建
console.log(this.props.oneCount)//100
console.log(this.state.message)//hello
this.oneMethod();//我是oneMethod方法
}
render(){
//在組件運行階段萍摊,componentWillUpdate()過后還會再次調用render()函數(shù),在render()執(zhí)行完畢之前如叼,頁面上的dom還是舊的冰木。
console.log(this.refs.h3 && this.refs.h3.innerHTML+'--------------運行階段調用render()時')
return <div>
<h3 id = "oneDom">這是一個Counter計數(shù)器組件</h3>
<button id="btn" onClick = {()=>this.changeData()}>點擊+1</button>
<hr/>
<h3 ref = 'h3'>當前的數(shù)量是{this.state.count}</h3>
</div>
}
componentDidMount(){
console.log(document.getElementById('oneDom'))
}
shouldComponentUpdate(nextProps,nextState){
//shouldComponentUpdate()判斷組件是否需要更新 返回布爾值 返回true則會調用render()重新渲染頁面,之后數(shù)據(jù)和頁面都是最新的
//如果返回false笼恰,不會執(zhí)行后續(xù)的生命周期函數(shù)踊沸,render()函數(shù)也不會調用,將會繼續(xù)返回組件的運行中的狀態(tài)社证,數(shù)據(jù)得到更新逼龟,組件的state狀態(tài)會被修改,但是頁面并沒有重新渲染追葡,是舊的腺律。
//在該組件中奕短,通過this.state.count拿到的屬性值是舊的,并不是最新的匀钧,在這里可以通過nextProps和nextState去獲取到對應的最新的屬性值
// return this.state.count%2?false:true
return nextState.count%2?false:true
//只有偶數(shù)時才更新頁面
}
//組件將要更新階段的狀態(tài)饥伊,在該狀態(tài)下梗肝,內(nèi)存中的虛擬dom和頁面上的dom還都是舊的冗锁,所以在該階段要謹慎操作dom庞萍,因為很可能只是操作的舊的Dom
componentWillUpdate(){
console.log(this.refs.h3.innerHTML+'---------------componentWillUpdate');
//打印出來的是舊dom的innerHTML
}
//組件完成了更新的狀態(tài),在該狀態(tài)下吊圾,數(shù)據(jù)和內(nèi)存中的虛擬dom以及頁面上的dom都是最新的达椰,此時可以放心大膽的去操作dom
componentDidUpdate(){
console.log(this.refs.h3.innerHTML+'---------------componentDidUpdate');
}
oneMethod(){
console.log('我是oneMethod方法')
}
changeData=()=>{
this.setState({
count:this.state.count+1
})
}
}
入口文件 index.js
// console.log("React真好用")
import React from 'react'
import ReactDom from 'react-dom'
import CounterFour from '@/components/CounterFour'
ReactDom.render(<div>
{/* 在這里規(guī)定每個使用該組件的用戶必須傳遞一個默認的數(shù)量值翰蠢,作為組件初始化的數(shù)據(jù) */}
<CounterFour oneCount = {0}></CounterFour>
</div>,document.getElementById('app'))
運行階段的componentWillReceiveProps()
該階段项乒,在組件將要接受外界傳遞過來的新的props屬性值時觸發(fā),組件第一次被渲染到頁面的時候是不會觸發(fā)該狀態(tài)的梁沧。只有通過某些事件檀何,修改了props屬性值之后,才會觸發(fā)廷支。
- 注意:在React16.3的版本中频鉴,componentWillReceiveProps更名為UNSAFE_componentWillReceiveProps在UNSAFE_componentWillReceiveProps被觸發(fā)的時候,如果通過this.props來獲取屬性值恋拍,獲得的是修改之前的屬性值垛孔。如果想要獲得最新的屬性值,要通過其參數(shù)列表來獲取施敢。UNSAFE_componentWillReceiveProps(nextProps)
如下:
import React from 'react'
export default class CounterFive extends React.Component{
constructor(props){
super(props)
this.state = {
message:'我是子組件'
}
}
render(){
return <div>
<h3>我是父組件</h3>
<button onClick = {()=>this.changeData()}>點擊改變數(shù)據(jù)</button>
<hr/>
<Son msg = {this.state.message}></Son>
</div>
}
changeData=()=>{
this.setState({
message:'哈哈哈周荐,哈哈哈'
})
}
}
class Son extends React.Component{
constructor(props){
super(props)
this.state = {}
}
render(){
return <div>
<h5>{this.props.msg}</h5>
</div>
}
//第一次渲染時是不會觸發(fā)該狀態(tài)的,在傳遞的參數(shù)被修改后才會觸發(fā)
UNSAFE_componentWillReceiveProps(nextProps){
//想要獲得最新的屬性值僵娃,要通過其參數(shù)列表來獲取
console.log(this.props.msg+'------'+nextProps.msg)
//我是子組件 -------哈哈哈概作,哈哈哈
}
}
15、React中綁定this傳參的三種方式
import React from 'react'
import DataTypes from 'prop-types'
export default class CounterSix extends React.Component{
constructor(props){
super(props)
this.state = {
message:'綁定this并傳參的幾種方式',
datamsg:'我是數(shù)據(jù)'
}
//綁定this并傳參的方式二:在構造函數(shù)中綁定并傳參
//當為一個函數(shù)綁定bind,改變this的指向后默怨,bind函數(shù)調用的結果讯榕,有一個返回值,這個返回值是改變this指向的函數(shù)的引用
this.changedata2 = this.changedata2.bind(this,1000,2000)
}
render(){
return <div>
<h3>{this.state.message}</h3>
{/* bind的作用匙睹,為前面的函數(shù)愚屁,修改函數(shù)內(nèi)部的this指向,讓函數(shù)內(nèi)部的this指向bind參數(shù)列表中的第一個參數(shù)痕檬。
bind和call和apply之間的區(qū)別
call和apply在修改完this的指向后會立即調用前面的函數(shù)
但是bind不會立即調用霎槐。bind參數(shù)列表中的第一個參數(shù)是用來修改this指向的,之后的參數(shù)谆棺,會被當做將來調用前面的函數(shù)的參數(shù)傳遞進去栽燕。 */}
<button onClick = {this.changedata.bind(this,100,200)}>綁定this并傳參的方式一</button>
<hr/>
<button onClick = {this.changedata2}>綁定this并傳參的方式二</button>
<hr/>
<button onClick = {()=>this.changedata3(200,500)}>綁定this并傳參的方式三</button>
<hr/>
<p>{this.state.datamsg}</p>
</div>
}
//值的注意的是罕袋,因為上面綁定處理方法的時候,使用了bind碍岔,所以這里可以不再使用箭頭函數(shù)浴讯。
changedata(num1,num2){
this.setState({
datamsg:'我被改變了'+num1+num2
})
}
changedata2(num1,num2){
this.setState({
datamsg:'我被改變了'+num1+num2
})
}
changedata3(num1,num2){
this.setState({
datamsg:'我被改變了'+num1+num2
})
}
}
16、一個評論列表的案例
-
采用組件化的方式蔼啦,創(chuàng)建一個評論列表 如下
index.js如下:
import React from 'react'
import ReactDom from 'react-dom'
import PList from '@/components/PList'
ReactDom.render(<div>
<PList></PList>
</div>,document.getElementById('app'))
發(fā)表評論的組件 Sendpl.jsx
import React from 'react'
export default class Sendpl extends React.Component{
constructor(props){
super(props)
this.state = {}
}
render(){
return <div>
<label htmlFor="pinglunren">評論人</label><br/>
<input type="text" name = "pinglunren" ref = 'pinglunren'/><br/>
<label htmlFor="contentBox">評論內(nèi)容</label><br/>
<textarea name="contentBox" id="contentBox" cols="30" rows="10" ref = "contentBox"></textarea>
<button onClick = {()=>this.Addpl()}>發(fā)表評論</button>
</div>
}
Addpl = ()=>{
//1榆纽、獲取評論人和評論內(nèi)容
//2、從本地存儲中獲取獲取之前的評論數(shù)組
//3捏肢、把最新的評論人和評論內(nèi)容放到數(shù)組中
//4奈籽、把最新的數(shù)組存儲在本地并清空相關區(qū)域
const content = {name:this.refs.pinglunren.value,words:this.refs.contentBox.value}
const pllist = JSON.parse(localStorage.getItem('lists')||'[]')
pllist.unshift(content)
localStorage.setItem('lists',JSON.stringify(pllist))
console.log(localStorage.getItem('lists'))
this.refs.pinglunren.value=this.refs.contentBox.value=''
//調用傳遞的getPL方法刷新評論列表
this.props.reloadlist()
}
}
評論列表組件Plitem.jsx組件
import React from 'react'
export default class Plitem extends React.Component{
constructor(props){
super(props)
this.state = {}
}
render(){
return <div style = {{border:'1px solid #ccc',margin:'15px 0'}}>
<h3>評論人:{this.props.name}</h3>
<p>評論內(nèi)容:{this.props.words}</p>
</div>
}
}
組件間的嵌套關系 Plist.jsx
import React from 'react'
import Plitem from '@/components/Plitem'
import Sendpl from '@/components/Sendpl'
export default class PList extends React.Component{
constructor(props){
super(props)
this.state = {
list:[
{id:0,name:'tom',words:'hello'},
{id:1,name:'jack',words:'world'},
{id:2,name:'cat',words:'byebye'}
]
}
}
render(){
return <div>
{/* 評論標題 */}
<h3>評論列表</h3>
{/* 發(fā)表評論組件 */}
{/* 在react中傳遞給組件數(shù)據(jù)或者方法都可以使用this.props.屬性(或者方法名)來調用 ,這與Vue中數(shù)據(jù)傳遞使用props鸵赫,方法傳遞使用this.$emit(方法名)是有不同的*/}
{/* 在該組件點擊發(fā)表評論時應該再一次調用UNSAFE_componentWillMount中執(zhí)行的方法getPL()衣屏,刷新評論列表 */}
<Sendpl reloadlist = {this.getPL}></Sendpl>
{/* 評論列表組件 */}
{this.state.list.map(item=>{
return <Plitem {...item} key={item.name}></Plitem>
})}
</div>
}
//獲取評論數(shù)組
getPL=()=>{
var getpl = JSON.parse(localStorage.getItem('lists')||'[]')
this.setState({
list:getpl
})
}
//虛擬Dom掛載到頁面之前調用getPL該方法,從本地取出數(shù)據(jù)替換掉之前的假數(shù)據(jù)
UNSAFE_componentWillMount(){
this.getPL()
}
}
效果如下
17狼忱、React中的context特性
- 當一個組件內(nèi)的其他子組件需要使用父組件的數(shù)據(jù)時,為了避免不必要的繁瑣者吁,可以使用context特性
如下:
import React from 'react'
import ReactTypes from 'prop-types'
// export default class Father extends React.Component{
// constructor(props){
// super(props)
// this.state = {
// color:'red'
// }
// }
// render(){
// return <div>
// <h2>我是父組件</h2>
// <Son color = {this.state.color}></Son>
// </div>
// }
// }
// class Son extends React.Component{
// constructor(props){
// super(props)
// this.state = {}
// }
// render(){
// return <div>
// <h4>我是子組件</h4>
// <Grandson color = {this.props.color}></Grandson>
// </div>
// }
// }
// class Grandson extends React.Component{
// constructor(props){
// super(props)
// this.state = {}
// }
// render(){
// return <div style = {{color:this.props.color}}>
// <h5>我是孫子組件</h5>
// </div>
// }
// }
//以上案例的目的窘俺,是孫組件如果想用到父組件的state里的值,
//是經(jīng)過了多次傳導才得到的复凳,而且子組件并沒有使用該值瘤泪,但是也參與了其中,這樣子看上去過于繁瑣染坯,所以為了避免有時候出現(xiàn)這種狀況均芽,可以使用react中的Context的屬性掀宋,如下:。
export default class Father extends React.Component{
constructor(props){
super(props)
this.state = {
color:'red'
}
}
// react中Context的使用
//1仲锄、在父組件中劲妙,創(chuàng)建一個function,它有個固定的名稱,getChildContext,這個方法內(nèi)部返回一個對象儒喊,將需要共享給其他子組件的數(shù)據(jù)包含在其中侨颈。
//2余赢、需要使用屬性校驗,規(guī)定共享給子組件的數(shù)據(jù)的類型,childContextTypes
getChildContext(){
return {
color:this.state.color
}
}
static childContextTypes = {
color:ReactTypes.string
}
render(){
return <div>
<h2>我是父組件</h2>
<Son></Son>
</div>
}
}
class Son extends React.Component{
constructor(props){
super(props)
this.state = {}
}
render(){
return <div>
<h4>我是子組件</h4>
<Grandson></Grandson>
</div>
}
}
class Grandson extends React.Component{
constructor(props){
super(props)
this.state = {}
}
//使用父組件共享的數(shù)據(jù)時同樣也是必須先進行校驗
static contextTypes = {
color:ReactTypes.string
}
render(){
return <div>
<h5 style = {{color:this.context.color}}>我是孫子組件----{this.context.color}</h5>
</div>
}
}