Skip to content

Latest commit

 

History

History
398 lines (291 loc) · 12.4 KB

React_three.md

File metadata and controls

398 lines (291 loc) · 12.4 KB

!>这是自学React的第三个晚上,加油加油。

Ref的使用

以前的案例中,我们写了下面的代码,使用了e.target,这并不直观,也不好看。这种情况我们可以使用ref来进行解决。

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

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

<input 
  id='memeda' 
  className='input' 
  value={this.state.inputValue} 
  onChange={this.inputChange.bind(this)}
  ref = {input=>this.input=input}//input只是形参 就一个 代表当前Dom
  /> 	

绑定后可以把上边的类改写成如下代码:

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

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

ref使用中的坑

比如现在我们要用ref绑定取得要服务的数量,可以先用ref进行绑定。

<ul ref={res=>this.res=res}>{/* ref绑定*/}
  {/* dangerouslySetInnerHTML={{__html:v}} 使用Html语法解析list内容  标签就可以不写入内容了*/}
  {
    this.state.list.map((v,i)=>{ 
      return (
        <XiaojiejieItem 
          key={v+i} 
          index={i} 
          content={v}//向子组件传值 子组件使用this.props.content接收
          deleteItem = {this.deleteItem} />
      )
    })
  }
</ul>

绑定后可以在addList()方法中,获取当前<li>有几个.

 addList(){
    this.setState({
        list:[...this.state.list,this.state.inputValue],
        inputValue:''
    })
    //关键代码--------------start
    console.log(this.res.querySelectorAll('li').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.res.querySelectorAll('li').length)
    })
    //关键代码--------------end
}

现在到浏览器中查看代码,就完全正常了。


PropTypes的简单应用

我们在Xiaojiejie.js组件里传递了4个值,有字符串,有数字,有方法,这些都是可以使用PropTypes限制的。在XiaojiejieItem.js需要先引入PropTypes

import PropTypes from 'prop-types'

引入后,就可以在组件的下方进行引用了,需要注意的是子组件的最下面(不是类里边),写入下面的代码:

//检验值
XiaojiejieItem.propTypes={
    content:PropTypes.string,
    index: PropTypes.number,
    deleteItem: PropTypes.func
}

XiaojiejieItem.JS文件的代码。

import React, { Component } from 'react';
import PropTypes from 'prop-types';//数据校验 在子组件里面 检验父组件的值
class XiaojiejieItem extends Component {
    constructor(props){
        super(props)
        this.handleClick = this.handleClick.bind(this)//优化.bind(this)写法 性能优化
    }
    render() { 
        return ( 
            <li onClick={this.handleClick}>
            {this.props.avname}-为你服务{this.props.content}
            </li> 
         );
    }
    handleClick(){
        this.props.deleteItem(this.props.index) //父组件传递的方法和参数在子组件使用
    }
}
//检验值
XiaojiejieItem.propTypes={
    content:PropTypes.string,
    index: PropTypes.number,
    deleteItem: PropTypes.func,
    avname: PropTypes.string.isRequired
}
//默认值
XiaojiejieItem.defaultProps={
    avname:'苍井空'
}
export default XiaojiejieItem;

这时候你在浏览器中查看效果,是什么都看不出来的,你需要修改一个错误的校验。比如我们把index改为必须是字符串。

index:PorpTypes.string

这时候浏览器的console里就会报错了,报错信息如下:

Warning: Failed prop type: Invalid prop `index` of type `number` supplied to `XiaojiejieItem`, expected `string`.
    in XiaojiejieItem (at Xiaojiejie.js:28)
    in Xiaojiejie (at src/index.js:5)

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

 必传值的校验

比如现在我们加入一个avname的属性,并放入JSX中,就算不传递这个值也不会报错的。代码如下:

return ( 
  <li onClick={this.handleClick}>
  {this.props.avname}-为你服务{this.props.content}
</li> 
);

这时候代码是不会报错的,我们传不传无所谓。比如我们现在传一个属性过来。

<ul>
    {
        this.state.list.map((item,index)=>{
            return (
                <XiaojiejieItem 
                key={index+item}  
                content={item}
                index={index}
                avname='波多野结衣'
                deleteItem={this.deleteItem.bind(this)}
                />
            )
        })
    }
</ul>  

这时候页面显示正常了,但是怎样避免必须传递avname这个属性值?如果不传递就报错,这就需要使用isRequired关键字了,它表示必须进行传递,如果不传递就报错。

avname:PropTypes.string.isRequired

 使用默认值defaultProps

defalutProps就可以实现默认值的功能,比如现在把avname的默认值设置成"松岛枫" ,然后把avname的属性删除掉。

XiaojiejieItem.defaultProps = {
    avname:'松岛枫'
}

官方文档还有很多的,能得到比较全面的了解。


React生命周期

!> React的生命周期是非常重要的知识点!

img

?>上图即为React的生命周期,值得注意的是,在React16及后面的版本中,有些生命周期已经废弃了,名字前面加上了UNSAFE_前缀的生命周期都是会废掉的。请看下面的例子。

可以看到React生命周期的四个大阶段:

  • Initialization:初始化阶段。

  • Mounting: 挂载阶段。

  • Updation: 更新阶段。

  • Unmounting: 销毁阶段

生命周期函数的定义

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

?>特别的,constructor我们叫构造函数,它是ES6的基本语法。虽然它和生命周期函数的性质一样,但不能认为是生命周期函数。但是你要心里把它当成一个生命周期函数,我个人把它看成React的Initialization阶段,定义属性(props)和状态(state)。

Initialization 初始化阶段

constructor(props){
  //constructor是Es6的语法 自动执行 相当于React的初始化生命周期函数initialization
  console.log('initialization=====>初始化阶段')
}

Mounting挂载阶段

Mounting阶段叫挂载阶段,伴随着整个虚拟DOM的生成,它里边有三个小的生命周期函数,分别是:

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

UNSAFE_componentWillMount代码

//属于Dom挂载阶段Mounting的生命周期 
UNSAFE_componentWillMount() {
  console.log('UNSAFE_componentWillMount=====>组件将要挂载到页面的时刻')
};

componentDidMount代码

//属于Dom挂载阶段Mounting的生命周期
componentDidMount(){
  console.log('componentDidMount=====>组件挂载完成的时刻')
};

render代码

render() {
  //属于Dom挂载阶段Mounting的生命周期 
  console.log('render=====>组件挂载中的时刻',3)
}

这时候我们查看一下控制台,会为我们打出下面的

这个函数书写没有顺序

注意的问题

?>UNSAFE_componentWillMountcomponentDidMount这两个生命周期函数,只在页面刷新时执行一次,而render函数是只要有state和props变化就会执行。

Updation组件更新阶段

React生命周期中的Updation阶段,也就是组件发生改变的更新阶段,这是React生命周期中比较复杂的一部分,它有两个基本部分组成,一个是props属性改变,一个是state状态改变。

shouldComponentUpdate函数

shouldComponentUpdate函数会在组件更新之前,自动被执行。比如写入下面的代码:

//属于Updation的生命周期 必须return布尔值 
//为true执行UNSAFE_componentWillUpdate和render函数 false则不执行
shouldComponentUpdate(){
  console.log('shouldComponentUpdate=====>组件更新之前时刻',1)
  return true;
};

它要求返回一个布尔类型的结果,必须有返回值,这里就直接返回一个true了(真实开发中,这个是有大作用的)。

现在就可以在控制台console里看到结果了,并且结果是每次文本框发生改变时都会随着改变。如果你返回了false,这组件就不会进行更新了。 简单点说,就是返回true,就同意组件更新;返回false,就反对组件更新。

UNSAFE_componentWillUpdate函数

UNSAFE_componentWillUpdate在组件更新之前,但shouldComponenUpdate之后被执行。但是如果shouldComponentUpdate返回false,这个函数就不会被执行了。

//属于Updation的生命周期
UNSAFE_componentWillUpdate() {
  console.log('UNSAFE_componentWillUpdate=====>组件将要更新的时刻',2)
}

componentDidUpdate

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

//属于Updation的生命周期
componentDidUpdate(){
  console.log('componentDidUpdate=====>组件更新完成的时刻', 4)
}

为了方便我们看出结果,可以在每个函数后加上序号。当我们输入1可以看到控制台输出的结果如下:

UNSAFE_componentWillReceiveProps 函数

我们可以先在Xiaojiejie.js组件里写下这个函数,例如下面的代码。

//组件第一次存在于虚拟dom中函数是不会执行的
//如果存在虚拟dom中 函数才会被执行
UNSAFE_componentWillReceiveProps() {
  console.log('UNSAFE_componentWillReceiveProps')
}

这时候会发现函数什么时候都不会被执行,因为Xiaojiejie.js算是一个顶层组件,它并没接收任何的props。可以把这个函数移动到XiaojiejieItem.js组件中。

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

//组件第一次存在于虚拟dom中函数是不会执行的
//如果存在虚拟dom中 函数才会被执行
UNSAFE_componentWillReceiveProps() {
  console.log('UNSAFE_componentWillReceiveProps')
}

这个时候再预览,就会看到UNSAFE_componentWillReceiveProps执行了 ,下图当我输入1代表子组件会收到Props的时候

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

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

这个生命周期算是比较复杂的一个生命周期,需要花时间消化。

componentWillUnmount函数

这个函数是组件从页面中删除的时候执行,比如在XiaojiejieItem.js,写入下面的代码:

//组件被删除之后执行
componentWillUnmount() {
  console.log('componentWillUnmount--组件删除')
}

写完后,可以到浏览器终端中查看结果,当我们点击服务项,服务项被删除时,这个函数就被执行了。


@XiaoHuwei 2019-09-21 Hangzhou