一、React基础知识再出发(二)

       继《一、React基础知识再出发(一)》之后,本篇文章,我们接着学习React的基础知识。

一、PropType与DefaultProps

       我们已经知道,在React中父组件可以向子组件传递数据(state值和函数),那么在子组件中,我们可以不可以对从父组件传递过来的值做一些校验和设置默认值。肯定是可以的,可能过程会比vue中稍微复杂一点。
vue中校验参数和默认值设置

<script>
export default {
  props: {
    username: {
      type: string,
      default:function(){
        return 'antiai'
      }
    }
  }
}
</script> 

React参数校验和默认值设置

...
import PropTypes from 'prop-types'
class TodoItem extends Component{
  ...
// 定义值的类型
static propTypes = {
    content: PropTypes.string,
    deleteItem: PropTypes.func,
    index: PropTypes.number
    }
}
// 设置默认值
static defaultProps = {
     test: 'hello world'
}

       在React中对值的校验和默认值的设定,确实要比vue中的复杂一点。当然校验的规则还有很多,例如

static propTypes = {
    test:PropTypes.string.isRequired,
    test1:PropTypes.oneOfType([
            PropTypes.string,
            PropTypes.number,
            PropTypes.instanceOf(Message)
     ])
}

具体的逻辑请参照https://reactjs.org/docs/typechecking-with-proptypes.html

二、React中的ref(refrence)

       ref用来获取原生DOM节点,这一点跟vue中类似。不过在React中的使用会相对复杂一点。
定义ref

<input 
     className="input"
     id="insertArea"
     value={this.state.inputValue}
     onChange={this.handleInputChange}
     ref={(input) => {this.input = input}}
 />

       我们需要给ref传递一个函数,如上,this.ul就是input的原生节点。
使用ref

handleInputChange(e){
        let value = this.input.value
        this.setState({
            inputValue: value
        })
}

       在React中,我们是不推荐使用ref的方式直接操作原生DOM节点的,我们更加推荐使用通过操作state数据方式修改数据。ref虽然能够实现功能,使用它会存在一定的风险。比如动态获取列表元素的节点个数。示例代码如下:

return (
            <Fragment>
                <div>
                    <label htmlFor="insertArea">输入内容</label>
                    <input 
                    className="input"
                    id="insertArea"
                    value={this.state.inputValue}
                    onChange={this.handleInputChange}
                    ref={(input) => {this.input = input}}
                    />
                    <button onClick={this.handleBtnClick}>提交</button>
                </div>
                <ul ref={(ul) => {this.ul = ul}}>
                    {
                        this.getItem()
                    }
                </ul>
            </Fragment>
        )
handleBtnClick(){
        this.setState({
            list: [...this.state.list, this.state.inputValue],
            inputValue: ''
        })
        alert(this.ul.querySelectorAll('div').length)
    }

       如上代码,我们直接获取DOM节点的个数时,第一次为0,第二次才是1,这是因为this.setState是异步更新数据的。要解决这问题,我们可以按如下的操作,将获取节点的操作放在this.setState第二个参数中。

handleBtnClick(){
        this.setState({
            list: [...this.state.list, this.state.inputValue],
            inputValue: ''
        },() => {
            alert(this.ul.querySelectorAll('li').length)
        })
}

三、React中的生命周期钩子函数

       首先来看一下React生命周期go函数的图示吧。

React中的生命周期钩子函数

        乍一看,很多,vue中也就不过才8个生命周期函数,这里似乎有十几个,而且这个概念图的设计也没有vue的好看。有点头疼。但是当我们仔细看的时候,其实并没有我们想象中的那么难。从图中,我们将所有的钩子函数按照执行的时机分为四大类。分别是initation、Mounting、Updation、Ummounting。首先,我们需要明白生命周期钩子函数是什么?生命周期函数指在某一时刻组件会自动调用执行的函数下面,我们将逐一的学习这四个阶段中的钩子函数。

1、 初始化阶段(Initation)

       按照上面的对生命周期钩子函数的描述。构造函数constructor是应该是生命周期钩子函数,在组件被创建的时候调用,但是构造函数是JavaScript语言的特性,并非React独有,所以不能端是生命周期钩子函数。

2、 挂载阶段(Mounting)

       1、componentWillMount:在组件即将被挂载到页面的时刻自动执行
// 在组件即将被挂载到页面的时刻自动执行
    componentWillMount(){
        console.log('componentWillMount:','在组件即将被挂载到页面的时刻自动执行')
    }
       2、componentDidMount:组件被挂载到页面之后执行
componentDidMount(){
    console.log('componentDidMount:','组件被挂载到页面之后执行')
}

3、 更新阶段(Updation)

       1、shouldComponentUpdate:组件被更新之后自动执行

       这个方法需要返回一个boolean值,如果为true表示执行后面的操作,否则不执行后面的操作。

shouldComponentUpdate(){
    console.log('shouldComponentUpdate', '组件被更新之后自动执行')
    return true
}
       2、componentWillUpdate:

       组件被更新之前,她会自动执行,但是他在shouldComponentUpdate之后执行
       如果shouldComponentUpdate返回true它才执行
       如果shouldComponentUpdate返回false,这个函数就不会执行了

componentWillUpdate(){
     console.log('componentWillUpdate')
}
       3、componentDidUpdate:组件更新完成之后,他会被触发
componentDidUpdate(){
   console.log('componentDidUpdate', '组件更新完成后,它会被执行')
}
       4、componentWillReceiveProps:子组件接受到父组件参数的时候触发!

       一个组件要从父组件接受参数
       如果这个组件第一次存在与父组件中,不会执行
       如果这个组件之前已经存在父组件中,才会执行

componentWillReceiveProps(){
   console.log('componentWillReceiveProps', '子组件接受到父组件参数的时候触发!')
}

4、 解绑阶段(Ummounting)

       1、componentWillUnmount:当组件即将被从页面中踢出的时候,会被执行
componentWillUnmount(){
    console.log('Child componentWillUnmount')
}

       下面表格列出的是vue和react中生命周期钩子函数对比图:

React Vue 执行时机
- beforeCreate 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用
- created 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el property 目前尚不可用
componentWillMount beforeMount 组件即将被挂载到页面的时刻
render - -
componentDidMount mounted 组件被挂载到页面之后
shouldComponentUpdate - 组件被更新之后自动执行
componentWillUpdate beforeUpdate 组件被更新之后自动执行(shouldComponentUpdate返回为true的情况下)
componentDidUpdate updated 组件更新完成之后,他会被触发
componentWillReceiveProps - 子组件接受到父组件参数的时候
- destroyed 组件即将被从页面中踢出前的时候
componentWillUnmount destroyed 组件即将被从页面中踢出的时候

5、 生命周期钩子函数的使用场景

       通常情况下,父组件数据发生变化会触发其render函数的执行,同时子组件的render函数也会被执行,这样性能会很差,有没有好的办法呢。我们可以在shouldComponentUpdate这个生命周期钩子函数处理我们的逻辑,判断前后输入的值是否有改变,有改变则返回true,继续执行子组件下面的render函数。

shouldComponentUpdate(nextProps, nextState){
    if(nextProps.content !== this.props.content){
        return true
    } else {
        return false
    }
}

       但是,从我测试的结果来看,这个优化放似乎已经起不到作用了,react 内部或许已经做了优化,这一点需要稍后确认一下。

四、React中优化的方案

       1、在父子组件通信的时候,子组件使用shouldComponentUpdate钩子函数进行优化。
       2、在constructor中绑定函数的this调用

constructor(props){
    super(props)
    this.state = {
        inputValue: '',
        list: []
     }
     this.handleInputChange = this.handleInputChange.bind(this)
     this.handleBtnClick = this.handleBtnClick.bind(this)
     this.handleDelete = this.handleDelete.bind(this)
}

       3、采用虚拟DOM和Diff算法进行优化。
       4、使用this.setState进行异步操作。

this.setState({
     inputValue: value
})

五、React中的动画

       React中的动画跟vue中的动画使用方式相差不大,主要有下面几种方式:

       1、CSS过渡动画

       这种动画是动画实现的最简单的一种。主要使用css3的transition属性实现。

constructor(props){
    super(props)
    this.state = {
         show: true
    }
    this.handleToggle = this.handleToggle.bind(this)
}
render(){
        return (
            <Fragment>
                <p className={this.state.show ? 'show' : 'hide'}>css transtion animation</p>
                <button onClick={this.handleToggle}>Toggle</button>
            </Fragment>
        )
}
handleToggle(){
    this.setState({
         show: !this.state.show
    })
}
.show{
    opacity: 1;
    transition: all 1s ease-in;
}
.hide{
    opacity: 0;
    transition: all 1s ease-out;
}

       2、CSS动画效果

       这种方式跟上面的过渡动画相似,不同之处主要在于将transition替换成了@keyframes动画。

.show{
    animation: show-item 2s ease-in forwards;
}
.hide{
    animation: hide-item 2s ease-in forwards;
}
@keyframes show-item{
    0%{
        opacity: 0;
        color: red;
    }
    50%{
        opacity: 0.5;
        color: green;
    }
    100%{
        opacity: 1;
        color: blue;
    }
}
@keyframes hide-item{
    0%{
        opacity: 1;
        color: red;
    }
    50%{
        opacity: 0.5;
        color: green;
    }
    100%{
        opacity: 0;
        color: blue;
    }
}

       3、react-transition-group实现动画

       在处理一些复杂的动画场景的时候,上面两种动画会显得有点鸡肋,这时候,我们可以更加强大的动画库react-transition-group来完成我们的需求。

yan add react-transition-group
import { CSSTransition } from 'react-transition-group'

render(){
        return (
            <Fragment>
                <CSSTransition
                    in={this.state.show}
                    timeout={1000}
                    classNames='fade'
                    unmountOnExit
                    appear={true}
                    onEntered={(el) => {el.style.color='blue'}}>
                    <p>css transtion animation</p>
                </CSSTransition>
                <button onClick={this.handleToggle}>Toggle</button>
            </Fragment>
        )
    }

       上面的参数表示:

属性 作用
in 控制何时显示
timeout 动画的过渡时间
classNames 动画的过渡的名称
unmountOnExit 动画结束后会移除动画节点
onEntered 动画结束之后调用的生命周期钩子函数
appear 为true时第一次也会产生动画

       当然react-transition-group的属性不止这些,您可以自行查阅文档。另外,我们还需要有css的配合。关于css的类名的解释如下:

.fade-enter, .fade-appear{
    opacity: 0;
}
.fade-enter-active, .fade-appear-active{
    opacity: 1;
    transition: opacity 1s ease-in;
}
.fade-enter-done{
    opacity: 1;
}
.fade-exit{
    opacity: 1;
}
.fade-exit-active{
    opacity: 0;
    transition: opacity 1s ease-in;
}
.fade-exit-done{
    opacity: 0;
}
属性 作用时机
.fade-enter 入场动画第一帧
.fade-enter-active 入场动画第二帧到动画结束之前
.fade-enter-done 入场动画完成的那一刻
.fade-exit 出场动画第一帧
.fade-exit-active 出场动画第一帧到动画结束之前
.fade-exit-done 出场动画完成的那一刻
.fade-appear 首次加载也使用动画
.fade-appear-active 首次加载也使用动画

       4、react-transition-group实现多元素动画

       示例代码如下:

import { CSSTransition, TransitionGroup } from 'react-transition-group'
render(){
        return (
            <Fragment>
                <ul>
                <TransitionGroup>
                    {
                        this.state.list.map((item, index) => {
                            return (
                                <CSSTransition
                                    timeout={1000}
                                    classNames='fade'
                                    unmountOnExit
                                    appear={true}
                                    onEntered={(el) => {el.style.color='blue'}}
                                    key={index}>
                                    <li >{item}</li>
                                </CSSTransition>
                            )
                        })
                    }
                    </TransitionGroup>
                </ul>
                <button onClick={this.handleAddItem}>Toggle</button>
            </Fragment>
        )
 }
handleAddItem(){
        this.setState((prevState) => {
            return {
                list: [...prevState.list, 'item']
            }
        })
}

       这个地方也没有什么地方需要过多解释,如有问题,可在评论区交流,本篇文章结束。

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