react从0到1的探索记录03

Reac生命周期中组件创建阶段的过程详解

componentWillMount();

componentWillMount(): 组件将要挂载到页面时触发该函数,此时组件还没有挂载到页面上。而且内存中,虚拟的Dom结构还没有被创建出来,但是初始化时的默认属性和this.state的属性是可以在函数中访问到的,可以调用组件中定义的方法。类比Vue中生命周期的created()阶段。

注意点

ReactV16.3对生命周期的部分做了一些新的处理,为componentWillMount,componentWillReceiveProps,componentWillUpdate添加一个“UNSAFE_”前缀。 (这里,“不安全”不是指安全性,而是表示使用这些生命周期的代码将更有可能在未来的React版本中存在缺陷,特别是一旦启用了异步渲染)。
所以在16.3及以后的版本中用UNSAFE_componentWillMount来替代componentWillMount,不替换也可以但是在控制台会有警告。
详细内容:ReactV16.3即将更改的生命周期

render()

render():该阶段是在内存中创建虚拟Dom的阶段,整个过程结束后,内存中就已经创建完成了一个虚拟Dom,但是并未将其挂载到页面上,所以在这个阶段也是无法获取任何dom元素的。

componentDidMount()

组件已经挂载到了页面上后就进入了该阶段,在这个阶段中,页面已经存在可见的Dom元素了,在这个页面可以随心所欲的操作页面上的dom元素,在该阶段,在React中同样也可以用原生js来为元素绑定事件,尽管react中可以用原生js来为元素绑定事件,但是react中最好不要直接去操作dom元素,我们通常使用react提供的机制绑定事件。之后组件就进入了运行阶段。

React生命周期的创建阶段的各个回调函数的演示

组件CounterThree.jsx

import React from 'react'
//导入参数传递校验的模块
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 组件将要挂载到页面时触发该函数,此时组件还没有挂载到页面上
    //而且内存中,虚拟的Dom结构还没有被创建出来,但是初始化时的默认属性和this.state的属性是可以在函数中访问到的,可以调用组件中定义的方法
    UNSAFE_componentWillMount(){
        console.log(document.getElementById('oneDom'));//null 此时虚拟dom还未创建
        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还未创建完成,页面是空的,拿不到任何元素
        return <div>
            <h3 id = "oneDom">这是一个Counter计数器组件</h3>
            <button id="btn" onClick = {()=>this.changeData()}>点击+1</button>
            <hr/>
            <h3>当前的数量是{this.state.count}</h3>
        </div>
        //return 执行完毕之后,虚拟Dom结构已经创建完,但还没有挂载到页面上
    }
    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>
{/* 在这里规定每个使用该组件的用户必须传递一个默认的数量值,作为组件初始化的数据 */}
    <CounterThree oneCount = {100}></CounterThree>
</div>,document.getElementById('app'))

Reac生命周期中组件运行阶段的过程详解

shouldComponentUpdate(nextProps,nextState);

shouldComponentUpdate()判断组件是否需要更新 返回布尔值;
返回true则会调用render()重新渲染页面,之后数据和页面都是最新的;
如果返回false,不会执行后续的生命周期函数,render()函数也不会调用,将会继续返回组件的运行中的状态,数据得到更新,组件的state状态会被修改,但是页面并没有重新渲染,是旧的。
在该组件中,通过this.state.count拿到的属性值是旧的,并不是最新的,在这里可以通过nextProps和nextState两个参数去获取到对应的最新的属性值。

componentWillUpdate()

组件将要更新阶段的状态,在该状态下,内存中的虚拟dom和页面上的dom还都是旧的,所以在该阶段要谨慎操作dom,因为很可能只是操作的旧的Dom。

render()

运行阶段再一次执行render()函数。在组件运行阶段,componentWillUpdate()过后还会再次调用render()函数,在render()执行完毕之前,页面上的dom还是旧的。

componentDidUpdate()

组件完成了更新的状态,在该状态下,数据和内存中的虚拟dom以及页面上的dom都是最新的,此时可以放心大胆的去操作Dom。

组件CounterFour.jsx

import React from 'react'
//导入参数传递校验的模块
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还未创建
        console.log(this.props.oneCount)//100
        console.log(this.state.message)//hello
        this.oneMethod();//我是oneMethod方法
    }
    render(){
        //在组件运行阶段,componentWillUpdate()过后还会再次调用render()函数,在render()执行完毕之前,页面上的dom还是旧的。
        console.log(this.refs.h3 && this.refs.h3.innerHTML+'--------------运行阶段调用render()时')

        return <div>
            <h3 id = "oneDom">这是一个Counter计数器组件</h3>
            <button id="btn" onClick = {()=>this.changeData()}>点击+1</button>
            <hr/>
            <h3 ref = 'h3'>当前的数量是{this.state.count}</h3>
        </div>
    }
    componentDidMount(){
        console.log(document.getElementById('oneDom'))
    }
    shouldComponentUpdate(nextProps,nextState){
        //shouldComponentUpdate()判断组件是否需要更新 返回布尔值 返回true则会调用render()重新渲染页面,之后数据和页面都是最新的
        //如果返回false,不会执行后续的生命周期函数,render()函数也不会调用,将会继续返回组件的运行中的状态,数据得到更新,组件的state状态会被修改,但是页面并没有重新渲染,是旧的。
        //在该组件中,通过this.state.count拿到的属性值是旧的,并不是最新的,在这里可以通过nextProps和nextState去获取到对应的最新的属性值
        // return this.state.count%2?false:true
        return nextState.count%2?false:true
        //只有偶数时才更新页面
    }
    //组件将要更新阶段的状态,在该状态下,内存中的虚拟dom和页面上的dom还都是旧的,所以在该阶段要谨慎操作dom,因为很可能只是操作的旧的Dom
    componentWillUpdate(){
        console.log(this.refs.h3.innerHTML+'---------------componentWillUpdate');
        //打印出来的是旧dom的innerHTML
    }
    //组件完成了更新的状态,在该状态下,数据和内存中的虚拟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>
{/* 在这里规定每个使用该组件的用户必须传递一个默认的数量值,作为组件初始化的数据 */}
    <CounterFour oneCount = {0}></CounterFour>
</div>,document.getElementById('app'))
运行阶段的componentWillReceiveProps()

该阶段,在组件将要接受外界传递过来的新的props属性值时触发,组件第一次被渲染到页面的时候是不会触发该状态的。只有通过某些事件,修改了props属性值之后,才会触发。

  • 注意:在React16.3的版本中,componentWillReceiveProps更名为UNSAFE_componentWillReceiveProps在UNSAFE_componentWillReceiveProps被触发的时候,如果通过this.props来获取属性值,获得的是修改之前的属性值。如果想要获得最新的属性值,要通过其参数列表来获取。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()}>点击改变数据</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>
    }
    //第一次渲染时是不会触发该状态的,在传递的参数被修改后才会触发
    UNSAFE_componentWillReceiveProps(nextProps){
        //想要获得最新的属性值,要通过其参数列表来获取
        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:'我是数据'
        }
        //绑定this并传参的方式二:在构造函数中绑定并传参
        //当为一个函数绑定bind,改变this的指向后,bind函数调用的结果,有一个返回值,这个返回值是改变this指向的函数的引用
        this.changedata2 = this.changedata2.bind(this,1000,2000)
    }
    render(){
        return <div>
            <h3>{this.state.message}</h3>
            {/* bind的作用,为前面的函数,修改函数内部的this指向,让函数内部的this指向bind参数列表中的第一个参数。
            bind和call和apply之间的区别
            call和apply在修改完this的指向后会立即调用前面的函数
            但是bind不会立即调用。bind参数列表中的第一个参数是用来修改this指向的,之后的参数,会被当做将来调用前面的函数的参数传递进去。 */}
            <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,所以这里可以不再使用箭头函数。
    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、一个评论列表的案例

  • 采用组件化的方式,创建一个评论列表 如下


    可以发表评论的评论列表

    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'))

发表评论的组件 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">评论内容</label><br/>
            <textarea name="contentBox" id="contentBox" cols="30" rows="10" ref = "contentBox"></textarea>
            <button onClick = {()=>this.Addpl()}>发表评论</button>
        </div>
    }
    Addpl = ()=>{
        //1、获取评论人和评论内容
        //2、从本地存储中获取获取之前的评论数组
        //3、把最新的评论人和评论内容放到数组中
        //4、把最新的数组存储在本地并清空相关区域
        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>评论内容:{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>

            {/* 发表评论组件 */}
            {/* 在react中传递给组件数据或者方法都可以使用this.props.属性(或者方法名)来调用 ,这与Vue中数据传递使用props,方法传递使用this.$emit(方法名)是有不同的*/}
            {/* 在该组件点击发表评论时应该再一次调用UNSAFE_componentWillMount中执行的方法getPL(),刷新评论列表 */}
            <Sendpl reloadlist = {this.getPL}></Sendpl>


            {/* 评论列表组件 */}
            {this.state.list.map(item=>{
                return <Plitem {...item} key={item.name}></Plitem>
            })}
        </div>
    }
    //获取评论数组
    getPL=()=>{
        var getpl = JSON.parse(localStorage.getItem('lists')||'[]')
        this.setState({
            list:getpl
        })
    }
    //虚拟Dom挂载到页面之前调用getPL该方法,从本地取出数据替换掉之前的假数据
    UNSAFE_componentWillMount(){
        this.getPL()
    }
}

效果如下

react创建一个可以添加的评论列表

17、React中的context特性

  • 当一个组件内的其他子组件需要使用父组件的数据时,为了避免不必要的繁琐,可以使用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里的值,
//是经过了多次传导才得到的,而且子组件并没有使用该值,但是也参与了其中,这样子看上去过于繁琐,所以为了避免有时候出现这种状况,可以使用react中的Context的属性,如下:。


export default class Father extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            color:'red'
        }
    }
    // react中Context的使用
    //1、在父组件中,创建一个function,它有个固定的名称,getChildContext,这个方法内部返回一个对象,将需要共享给其他子组件的数据包含在其中。
    //2、需要使用属性校验,规定共享给子组件的数据的类型,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 = {}
    }
    //使用父组件共享的数据时同样也是必须先进行校验
    static contextTypes = {
        color:ReactTypes.string
    }
    render(){
        return <div>
            <h5 style = {{color:this.context.color}}>我是孙子组件----{this.context.color}</h5>
        </div>
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 作为一个合格的开发者,不要只满足于编写了可以运行的代码。而要了解代码背后的工作原理;不要只满足于自己的程序...
    六个周阅读 8,428评论 1 33
  • 原教程内容详见精益 React 学习指南,这只是我在学习过程中的一些阅读笔记,个人觉得该教程讲解深入浅出,比目前大...
    leonaxiong阅读 2,813评论 1 18
  • 40、React 什么是React?React 是一个用于构建用户界面的框架(采用的是MVC模式):集中处理VIE...
    萌妹撒阅读 1,005评论 0 1
  • 9、在react中如何创建组件 ES6中class关键字的作用 class关键字中构造器constructor的了...
    An的杂货铺阅读 333评论 0 0
  • React基础 React组件化编程 Create React App 创建React 前端工程 题外话题:页面性...
    BeautifulHao阅读 1,538评论 0 3