React使用 - 常用生命周期函数、受控组件与非受控组件

React生命周期
生命周期: 就是指某个事物从开始到结束的各个阶段。

React生命周期:在 React.js 中指的是组件从创建到销毁的过程,React.js 在这个过程中的不同阶段调用的函数。

作用:通过这些函数,我们可以更加精确的对组件进行控制。前面我们一直在使用的 render 函数其实就是组件生命周期渲染阶段执行的函数

注意:React生命周期的新旧方法之间,不可以同时存在。否则报错

生命周期函数详解
常用生命周期函数
1、挂载阶段
constructor(props)
类的构造函数,也是组件初始化函数,一般会在这个阶段做一些初始化的工作
(1) 初始化 state
(2) 处理事件绑定函数的 this

export default class Person extends Component {
    constructor(props) {
        super(props);
        this.state = { name: 'tom', age: 18 }
        this.clickFn = this.clickFn.bind(this);
    }
    clickFn() {
        console.log(this);
        this.setState({ nickname: '哈皮' });
    }
    render() {
        let { name, nickname } = this.state;
        return (
            <div>
                <p>{name}</p>
                <p>{nickname}</p>
                <button onClick={
                    this.clickFn
                }>
                    点击按钮
         </button>
            </div>
        );
    }
}

render()
render 方法是 Class 组件必须实现的方法

 export default class Test extends Component {
     render() {
         return (<div>hello,good morning</div>);
     }
 }

2、更新阶段
static getDerivedStateFromProps(props, state)
该方法会在 render 方法之前调用,无论是挂载阶段还是更新阶段,
它的存在只有一个目的:让组件在 props 变化时更新 state

export default class App extends Component {
    state = {
        address: '中国',
        city: '北京'
    }
    static getDerivedStateFromProps(nextProps, prevState) {
        //console.log(nextProps);
        //console.log(prevState);
        if (nextProps.city !== prevState.city) {
            return {
                city: nextProps.city
            }
        }
        return null;
    }
    updateAddress = () => {
        this.setState({
            city: '天津'
        })
    }
    render() {
        let { city } = this.state;
        return (
            <div>
                amazing
                <p>{city}</p>
                <button onClick={
                    () => {
                        this.updateAddress()
                    }}>
                    切换城市
                </button>
            </div>
        );
    }
}

componentDidMount()
在组件挂载后(render 的内容插入 DOM 树中)调用。
通常在这个阶段,我们可以:
(1) 操作 DOM 节点
(2) 发送请求


export default class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            fontSize: 15,
            color: 'gray'
        }
    }
    rmColor = () => {
        let r = Math.floor(Math.random() * 255);
        let g = Math.floor(Math.random() * 255);
        let b = Math.floor(Math.random() * 255);
        return 'rgb(' + r + ',' + g + ',' + b + ')';
    }
    // 组件渲染后调用
    componentDidMount() {
        let num = 5;
        // 设置定时器
        let timer = setInterval(() => {
            console.log(num);
            let { fontSize } = this.state;

            if (fontSize > 30) {
                fontSize = 15;
            }
            // 每次自增5
            this.setState({
                fontSize: fontSize + 5,
                color: this.rmColor()
            });

            num--;
            if (num < 1) {
                clearInterval(timer);
            }
        }, 500);

        // 如果定时器那里不使用箭头函数,就需要在定时器中绑定this
        // }.bind(this), 500);
    }
    render() {
        return (
            // 将新值赋给当前的fontSize
            <div style={
                {
                    fontSize: this.state.fontSize + 'px',
                    color: this.state.color
                }
            }>
                Hello! Welcome to Beijing
            </div>
        );
    }
}

shouldComponentUpdate(nextProps, nextState)
(1) 发生在更新阶段,getDerivedStateFromProps 之后,render 之前,
(2) 该函数返回一个布尔值,默认返回true,它决定了后续是否执行render方法
(3) 首次渲染时不会调用该函数
(4) 使用场景:
一般情况下,react的父级组件的render函数重新渲染会引起子组件的render方法的重新渲染。
但有时子组件接收父组件的数据后没有变动。如果子组件此时不需要变动还是执行了render方法,
跟着父组件重新渲染了,这就会影响程序性能,而这时就可以使用shouldComponentUpdate来解决这个问题。

//父级
//当在父级组件中调用setState方法时,它的子组件Book都会重新执行render方法去再次渲染
export default class App extends Component {
    constructor(props) {
        super();
        this.state = {
            kind: '分类',
        };
    }

    componentDidMount() {
        this.setState({
            kind: '新的分类',
        });
    }

    render() {
        return (
            <div>
                <Book />
                <div>{this.state.kind}</div>
            </div>
        );
    }
}
// 子级
// 当子组件Book中的state并未作任何改变,所以不需要再随父级而重新渲染
// 添加shouldComponentUpdate()函数,相当于先做判断,如果返回true,再次执行render方法去重新渲染,否则不再重新渲染。
export class Book extends Component {
    constructor(props) {
        super();
        this.state = {
            name: 'React前端框架'
        }
    }
    shouldComponentUpdate(nextProps, nextState) {
        console.log(nextProps);
        console.log(nextState);
        return nextState.name !== this.state.name;
    }
    render() {
        return (
            <div>
                {this.state.name}
            </div>
        );
    }
}

getSnapshotBeforeUpdate(prevProps, prevState)

(1) 该方法在 render() 之后,但是在输出到 DOM 之前执行,用来获取渲染之前的快照。
(2) 当我们想在当前一次更新前获取上次的 DOM 状态,可以在这里进行处理,该函数的返回值将作为参数传递给下个生命周期函数
(3) 必须return一个结果或者返回null
getSnapshotBeforeUpdate()DOM更新之前,将立即调用新的生命周期方法。此方法的返回值将作为第三个参数传递给componentDidUpdate()

componentDidUpdate()
该函数会在 DOM 更新后立即调用,首次渲染不会调用该方法。我们可以在这个函数中对渲染后的 DOM 进行操作

export default class App extends Component {
    constructor(props) {
        super(props);
        // 使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,
        // 该ref的current属性将能拿到dom节点或组件的实例
        this.listRef = React.createRef();
    }

    getSnapshotBeforeUpdate(prevProps, prevState) {
        // 捕获滚动的位置,以便后面进行滚动 注意返回的值
        if (prevProps.list.length < this.props.list.length) {
            let list = this.listRef.current;
            return list.scrollHeight - list.scrollTop;
        }
        return null;
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        // 如果有 snapshot 会进行滚动的调整,这样子就不会立即将之前的内容直接弹上去
        if (snapshot !== null) {
            let list = this.listRef.current;
            list.scrollTop = list.scrollHeight - snapshot;
        }
    }

    render() {
        return (
            <div ref={this.listRef} style={{ 
            height: '150px', width: '300px', backgroundColor: 'lightblue', overflow: 'auto' }}>
                jhvdjhhsdjvdlsd;f'asjdfklsadfljksdkv,vkljiuerygdokosdgjsodjgsd
                dkgosdjgojsdlgksdgesrg;sdkfsod;klvkdsovsedkborkrtlfgkbfdposdksd;psdlsdfd
                dfkdsjkjlkspgoiuohpksdlvjouhs;vl2q373r7734989490i20i3tu794554'4tu45u
                jkdgsldk;kou45oerkhldfhikrth;;erlhldkfghue5ir0yi56ut;jtjpt
                eougieudlvz,hahd7236879405496--ul;,bdljvkdisuf98fw347t9rltr;
                eig9ut0iephekdfmljsdd';slt9058307582739ruweosglsdgoeur9tgrt78356877487
                skjsdg,md,jsdjvljsdogjlsdklg sdvhdkgkdllgdslkgkdljgldvjgjdfjlgdf
                dkbhdkgkvdshjhsdkhgsjdkfglsdfieysdffuisdfjvjdsjvhjhsdkjjvkjxdjgserd
            </div>
        );
    }
}

3、卸载阶段
componentWillUnmount()
(1) 该方法会在组件卸载及销毁前调用,
(2) 在每一个组件执行render方法前调用
(3) 在浏览器端和服务器端都可以调用
我们可以在这里做一些清理工作,如:组件内的定时器、未完成的请求等

官网中时钟示例
组件内先执行constructor,然后执行render,将虚拟dom插入到组件中,并渲染初始状态到页面中;
然后执行挂载钩子函数,启动计时器,每隔一秒执行一次自定义的timeFn方法,组件会调用setState方法去重新渲染页面;
最后将计时器从组件中清除,此时调用componentWillUnmount() 卸载生命周期函数,终止计时器。

export default class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            date: new Date()
        };
    }
    //挂载钩子函数
    componentDidMount() {
        this.timerID = setInterval(
            () => this.timeFn(),1000
        );
    }
    //卸载钩子函数
    componentWillUnmount() {
        clearInterval(this.timerID);
    }
    //时间处理方法
    timeFn() {
        this.setState({
            date: new Date()
        });
    }
    // 渲染
    render() {
        return (
            <div>
                <h2>当前时间:{this.state.date.toLocaleTimeString()}</h2>
            </div>
        );
    }
}

4、错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法
(1) static getDerivedStateFromError()
(2) componentDidCatch(error,info)

import React,{Component} from 'react';
export default class App extends Component {
   state = {
      hasError:true
   } 
   static getDerivedStateFromError(error) {
      return { hasError: true };
   }
   componentDidCatch(error,info) {
     this.setState({hasError:true});
     console.log(info);
   }

   render(){
      if(this.state.hasError){
         return (<div>报错了</div>);
       }
      return this.props;
   }
}

受控组件与非受控组件
受控组件:类似vue的双向数据绑定,数据和视图之间可以相互影响
使用input输入框,如果需要取值并且需要修改它时,需要使用value结合onChange事件。
非受控组件:类似单向数据流,只可以使用数据改变视图
使用input输入框时,如果只需要取值时,使用defaultValue就可以。

import React, { Component } from 'react';
export default class App extends Component {
    state = {
        info: '填写内容'
    }

    render() {
        let { info } = this.state;
        return (
            <div>
                {/* 无论是单、双标签,结尾都需要闭合 */}
                {/* 
                 */}

                {/* 如果input输入框不写onChange事件,那么输入框的值只是可读的,不能修改 */}
                <input type="text"
                    value={info}
                    onChange={({ target }) => {
                        this.setState({
                            info: target.value
                        })
                      }
                   }/>

                <button onClick={
                    () => {
                        if (info === null || info === "") {
                            alert("输入框不能为空");
                        } 
                        console.log(info);
                    }}>
                    点击
                </button>
            </div>
        )
    }
}

你可能感兴趣的:(React,react,react生命周期函数,react受控组件与非受控组件)