React高级(上):调试工具、PropTypes校验、Ref简单使用、React生命周期

React调试工具的安装和使用

现在已经可以写一些简单的React代码了
但经常使用console.log来调成程序显得很傻

React在浏览器端是有一个调试工具,React developer tools,这是React人比必下的一个调试工具!

React developer tools的三种状态

React developer tools有三种颜色,三种颜色代表三种状态

  1. 灰色: 这种就是不可以使用,说明页面不是用React编写的
  2. 黑色: 说明页面是用React编写的,并且处于线上环境
  3. 红色: 说明页面使用React编写的,并且处于调试环境当中

React developer tools使用

打开浏览器,F12进入开发者工具,在面板的最后一个,会有一个React,这就是安装的插件了

在这里可以清晰地看到React的结构,让自己写的代码更加清晰,还可以看到组件间的数据传递,就不需要用console.log来测试程序了

PropTypes校验传递值

在父组件向子组件传递数据时,使用了属性的方式,也就是props

但“小姐姐服务菜单”的案例并没有任何的限制,这在工作中正式开发是完全不允许的,大型项目,如果不校验,后期会变得异常混乱,业务逻辑也没法保证

PropTypes简单应用

<XiaojiejieItem
    index={index}
    key={index+item}
    deleteItem={this.deleteItem.bind(this)}
    content={item} />

Xiaojiejie.js父组件里向XiaojiejieItem.js子组件传递了4个值,有字符串,有数字,有方法

这些都可以使用PropTypes进行限制,首先进行引入

  • 父组件传递给子组件,肯定是在子组件里做校验

import PropTypes from 'prop-types';

引入后,就可以在组件的下方进行引用了(是在子组件的最下面。不是类里边)

XiaojiejieItem.propTypes = {
    content:PropTypes.string,
    index:PropTypes.number,
    deleteItem:PropTypes.func
}

浏览器查看效果,发现都正常,我们修改一个错误的校验,把index改为必须是字符串

index:PropTypes.string

这时浏览器的console里就会有报错信息

index.js:1 Warning: Failed prop type: Invalid prop `index` of type `number` supplied to `XiaojiejieItem`, expected `string`.

意思就是要求传递字符串,而我们却传递了数字过去,所以给了警告

必传值的校验

XiaojiejieItem加入一个personName属性,并放入JSX
现在就算Xiaojiejie不给XiaojiejieItem传递这个值也不会报错

{this.props.personName}为您服务-{this.props.content}

现在我们从Xiaojiejie传一个属性过来

<XiaojiejieItem
  index={index}
  key={index + item}
  deleteItem={this.deleteItem.bind(this)}
  content={item}
  personName='景甜'
  />

这时候页面显示正常了

那我们如何避免必须传递personName这个属性值?

如果不传递就报错,就需要使用isRequired关键字,它表示必须进行传递,如果不传递就报错
personName:PropTypes.string.isRequired

Warning: Failed prop type: The prop `personName` is marked as required in `XiaojiejieItem`, but its value is `undefined`.

使用默认值defaultProps

defaultProps可以实现默认值的功能

XiaojiejieItem.defaultProps = {
    personName:'黎明'
}

这样就算父组件不传personName这个属性的话,子组件也会有默认值黎明

Ref的使用方法

在编写组件中的方法时,经常会遇到语义化模糊的代码

这对团队开发是很大的问题,code review或者合作时都会影响开发效率,必须重视react代码中的语义化。

代替原来的e.target.value

inputChange(e) {
    this.setState({
        inputValue: e.target.value
    })
}

使用e.target并不直观,也不好看,我们可以使用ref来进行解决

要使用ref,需要在JSX中进行绑定,绑定时最好使用ES6语法中的箭头函数,这样可以简洁明了的绑定DOM元素

<input 
    id="serve" 
    className="input" 
    value={this.state.inputValue} 
    onChange={this.inputChange.bind(this)}
    //关键代码——----------start
    ref={(input)=>{this.input=input}}
    //关键代码------------end
    />

绑定后可以把上面的方法做如下修改

inputChange(e) {
    this.setState({
        inputValue: this.input.value
    })
}

这就使我们使我们的代码变得语义化和优雅一些,但是不建议用ref这样操作,React是数据驱动的,用ref会出现各种问题

ref使用的坑

要用ref绑定取得要服务的数量,先用ref进行绑定

<ul ref={(ul)=>{this.ul=ul}>
  {
      this.state.list.map((item, index) => {
          return (
              <XiaojiejieItem
                  index={index}
                  key={index + item}
                  deleteItem={this.deleteItem.bind(this)}
                  content={item}                     
              />
          )
      })
  }
</ul>

绑定后可以在addList()方法中,获取当前<div>的值(看自己代码,子组件用的是div,不是li))

 addList() {
    this.setState({
        list: [...this.state.list, this.state.inputValue],
        inputValue: ''
    })
//关键代码--------------start
console.log(this.ul.querySelectorAll('div').length)
//关键代码--------------end
}

打开控制台,点击添加服务按钮,会发现返回数量少了一个

这个坑是因为React中的setState是一个异步函数所这构成的

也就是说,setState代码执行是有一个时间的,这里涉及到虚拟DOM,简单说,就是因为异步,还没有等虚拟DOM渲染,console.log就已经执行了

怎么编写才会正常呢?setState方法提供了一个回调函数,也就是它的第二个函数

addList() {
      this.setState({
          list: [...this.state.list, this.state.inputValue],
          inputValue: ''
      },()={
  //关键代码--------------start
  console.log(this.ul.querySelectorAll('div').length)
  //关键代码--------------end
  })	
}

这样写就正常显示了

React生命周期

React声明周期的四大阶段

  1. Initialization 初始化阶段
  2. Mounting 挂载阶段
  3. Updation 更新阶段
  4. Unmounting 销毁阶段

什么是生命周期函数

生命周期函数指在某一个时刻组件会自动调用执行的函数

Xiaojiejie.js中,render()函数就是一个生命周期函数,它在state发生改变时自动执行

constructor不算生命周期函数,它叫构造函数,是ES6的基本语法

可以把它看成React的Initialization阶段,定义属性(props)和状态(state)

Mounting阶段

Mounting阶段叫挂载阶段,伴随着整个虚拟DOM的生成

它有三个小的生命周期函数

  1. componentWillMount:在组件即将被挂载到页面的时刻执行
  2. render:页面state或props发生变化时执行
  3. componentDidMount:组件挂载完成时被执行

我们在Xiaojiejie.js中编写三个生命周期函数来查看下

componentWillMount() {
    console.log('componentWillMount---组件将要挂载到页面的时刻')
}
componentDidMount() {
    console.log('componentDidMount---组件挂载完成的时刻执行')
}
render() {
    console.log('render---开始挂载渲染---')
}

查看下控制台

componentWillMount---组件将要挂载到页面的时刻

render---开始挂载渲染---

react-dom.development.js:67 Warning: componentWillMount has been renamed, and is not recommended for use. See 
* Move code with side effects to componentDidMount, and set initial state in the constructor.
* Rename componentWillMount to UNSAFE_componentWillMount to suppress this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.

componentDidMount---组件挂载完成的时刻执行

这也是生命周期的顺序,但是在js中书写这三个生命周期函数是没有顺序的,哪个在前哪个在后,可以随便更改

注意

componentWillMountcomponentDidMount这两个生命周期函数,只在页面刷新时执行一次

render函数只要有state和props变化就会执行

Updation阶段

这个阶段就是组件发生改变的更新阶段,是React生命周期比较复杂的一部分

有两个基本部分组成,一个是props属性改变,一个是state状态改变

shouldComponentUpdate函数

shouldComponentUpdate函数会在组件更新之前,自动被执行

shouldComponentUpdate() {
      console.log('shouldComponentUpdate---组件发生改变前执行')
  }

它要求返回一个布尔类型的结果,必须有返回值,不然就会报错

Warning: Xiaojiejie.shouldComponentUpdate(): Returned undefined instead of a boolean value. Make sure to return true or false.

返回一个true

shouldComponentUpdate() {
      console.log('shouldComponentUpdate---组件发生改变前执行')
      return true
  }

可以在控制台看到结果了,并且结果是每次文本框发生改变时都会随着改变

shouldComponentUpdate---组件发生改变前执行

render---组件挂载中---

如果返回false,组件就不会更新了

总的来说,返回true,就同意组件更新,返回false,就反对组件更新

componentWillUpdate函数

componentWillUpdate在组件更新之前,但是在shouldComponentUpdate之后被执行

如果shouldComponentUpdate返回false,这个函数就不会被执行了

shouldComponentUpdate---组件发生改变前执行
componentWillUpdate--组件更新前,shouldComponentUpdate函数之后
 render---组件挂载中---

componentDidUpdate函数

componentDidUpdate在组件更新之后执行,它是组件更新的最后一个环节

为了方便查看,在每个函数前面加上序号

1-shouldComponentUpdate---组件发生改变前执行
2-componentWillUpdate--组件更新前,shouldComponentUpdate函数之后 
3-render---开始挂载渲染,组件挂载中
4-componentDidUpdate---组件更新之后执行

componentWillReceiveProps函数

我们先在Xiaojiejie.js组件里写下这个函数

componentWillReceiveProps() {
      console.log('componentWillReceiveProps')
  }

会发现这个函数什么时候都不会被执行

因为Xiaojiejie.js是一个顶层组件,它并没有接收任何的props,可以把这个函数移动到XiaojiejieItem.js组件中

凡是组件都有生命周期函数,所以子组件也是有的,并且子组件接收了props,这时这个函数就可以被执行了

componentWillReceiveProps() {
        console.log('child - componentWillReceiveProps')
    }
1-shouldComponentUpdate---组件发生改变前执行
2-componentWillUpdate--组件更新前,shouldComponentUpdate函数之后
3-render---组件挂载中---
child - componentWillReceiveProps
4-componentDidUpdate---组件更新之后执行

总结一下,子组件接收到父组件传递过来的参数,父组件render函数重新被执行,这个生命周期就会被执行

  • 这个组件第一次存在于Dom中,函数是不会被执行的
  • 如果已经存在于Dom中,函数才会被执行

Unmounting阶段

componentWillUnmount函数

这个函数在组件从页面中删除的时候执行

XiaojiejieItem.js中写

// 当组件从页面中删除的时候执行
componentWillUnmount() {
    console.log('child - componentWillUnmount')
}

点击服务项,服务项被删除时,这个函数就被执行了

1-shouldComponentUpdate---组件发生改变前执行
2-componentWillUpdate--组件更新前,shouldComponentUpdate函数之后
3-render---组件挂载中---
child - componentWillReceiveProps
child - componentWillUnmount
4-componentDidUpdate---组件更新之后执行

生命周期改善程序性能

小姐姐组件存在性能问题

子组件XiaojiejieItem频繁无用渲染render

在React Developer Tools中点开设置,选中highlight Updates

这时在浏览器的文本框中输入内容,可以清楚地看到子组件也发生了重新render的情况
很多人会忽略这样的性能损耗,认为没有什么大不了的,但是软件的卡顿是一点点产生的,所以必须要减少性能损耗

可以在XiaojiejieItem.jsrender函数里加入下面代码方便查看子组件渲染的情况

 render() { 
        console.log('child-render')
        return (  
            <div onClick={this.handleClick}>
                {this.props.personName}为您服务-{this.props.content}
            </div>
        );
    }

利用shouldComponentUpdate解决

直接在XiaojiejieItem.js中加入下面的代码

shouldComponentUpdate() {
        return false;
    }

现在问题已经没有了,但这样做太暴力,否定了所有的东西

在真实项目中,增删改查肯定是需要的,需要改变值的属性值,达到渲染就没办法了

我们可以优化一下,当添加一项服务时,子组件才会渲染

shouldComponentUpdate有两个参数:

  • nextProps:变化后的属性
  • nextState:变化后的状态
shouldComponentUpdate(nextProps,nextState) {
        if(nextProps.content !== this.props.content){
            return true
        }else {
            return false
        }
    }

这算是完美解决了子组件的渲染性能问题
面试React时,写TODOList应用,都是看这个类区分等级的
能写出来的,算是普通程序员
能写出来并做性能优化的,这算有经验的程序员

参考资料

https://www.jianshu.com/p/514fe21b9914
https://jspang.com/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值